cc-claw 0.22.7 → 0.23.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/cli.js +1983 -1293
- package/package.json +6 -2
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.23.0" : (() => {
|
|
37
37
|
try {
|
|
38
38
|
return JSON.parse(readFileSync(join(process.cwd(), "package.json"), "utf-8")).version ?? "unknown";
|
|
39
39
|
} catch {
|
|
@@ -11793,8 +11793,8 @@ var init_memory = __esm({
|
|
|
11793
11793
|
return jsonResponse(res, { success: deleted, mode: "id" });
|
|
11794
11794
|
}
|
|
11795
11795
|
if (body.keyword) {
|
|
11796
|
-
const { forgetMemory:
|
|
11797
|
-
const count =
|
|
11796
|
+
const { forgetMemory: forgetMemory4 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
|
|
11797
|
+
const count = forgetMemory4(body.keyword);
|
|
11798
11798
|
return jsonResponse(res, { success: true, count, mode: "keyword" });
|
|
11799
11799
|
}
|
|
11800
11800
|
jsonResponse(res, { error: "Either 'keyword' or 'memoryId' is required" }, 400);
|
|
@@ -12189,8 +12189,8 @@ var init_config = __esm({
|
|
|
12189
12189
|
handleModelSet = async (req, res) => {
|
|
12190
12190
|
try {
|
|
12191
12191
|
const body = JSON.parse(await readBody(req));
|
|
12192
|
-
const { setModel:
|
|
12193
|
-
|
|
12192
|
+
const { setModel: setModel5, clearThinkingLevel: clearThinkingLevel4, clearSession: clearSession3 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
|
|
12193
|
+
setModel5(body.chatId, body.model);
|
|
12194
12194
|
clearThinkingLevel4(body.chatId);
|
|
12195
12195
|
clearSession3(body.chatId);
|
|
12196
12196
|
logActivity(getDb(), { chatId: body.chatId, source: "cli", eventType: "config_changed", summary: `Model set to ${body.model}`, detail: { field: "model", value: body.model } });
|
|
@@ -12202,8 +12202,8 @@ var init_config = __esm({
|
|
|
12202
12202
|
handleThinkingSet = async (req, res) => {
|
|
12203
12203
|
try {
|
|
12204
12204
|
const body = JSON.parse(await readBody(req));
|
|
12205
|
-
const { setThinkingLevel:
|
|
12206
|
-
|
|
12205
|
+
const { setThinkingLevel: setThinkingLevel5 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
|
|
12206
|
+
setThinkingLevel5(body.chatId, body.level);
|
|
12207
12207
|
logActivity(getDb(), { chatId: body.chatId, source: "cli", eventType: "config_changed", summary: `Thinking set to ${body.level}`, detail: { field: "thinking", value: body.level } });
|
|
12208
12208
|
jsonResponse(res, { success: true });
|
|
12209
12209
|
} catch (err) {
|
|
@@ -12244,8 +12244,8 @@ var init_config = __esm({
|
|
|
12244
12244
|
handlePermissionsSet = async (req, res) => {
|
|
12245
12245
|
try {
|
|
12246
12246
|
const body = JSON.parse(await readBody(req));
|
|
12247
|
-
const { setMode:
|
|
12248
|
-
|
|
12247
|
+
const { setMode: setMode5 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
|
|
12248
|
+
setMode5(body.chatId, body.mode);
|
|
12249
12249
|
logActivity(getDb(), { chatId: body.chatId, source: "cli", eventType: "config_changed", summary: `Permissions set to ${body.mode}`, detail: { field: "permissions", value: body.mode } });
|
|
12250
12250
|
jsonResponse(res, { success: true });
|
|
12251
12251
|
} catch (err) {
|
|
@@ -12255,12 +12255,12 @@ var init_config = __esm({
|
|
|
12255
12255
|
handleToolsToggle = async (req, res) => {
|
|
12256
12256
|
try {
|
|
12257
12257
|
const body = JSON.parse(await readBody(req));
|
|
12258
|
-
const { toggleTool:
|
|
12258
|
+
const { toggleTool: toggleTool6 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
|
|
12259
12259
|
if (body.enabled !== void 0) {
|
|
12260
12260
|
const db3 = getDb();
|
|
12261
12261
|
db3.prepare("INSERT OR REPLACE INTO chat_tools (chat_id, tool, enabled) VALUES (?, ?, ?)").run(body.chatId, body.tool, body.enabled ? 1 : 0);
|
|
12262
12262
|
} else {
|
|
12263
|
-
|
|
12263
|
+
toggleTool6(body.chatId, body.tool);
|
|
12264
12264
|
}
|
|
12265
12265
|
jsonResponse(res, { success: true });
|
|
12266
12266
|
} catch (err) {
|
|
@@ -12270,8 +12270,8 @@ var init_config = __esm({
|
|
|
12270
12270
|
handleToolsReset = async (req, res) => {
|
|
12271
12271
|
try {
|
|
12272
12272
|
const body = JSON.parse(await readBody(req));
|
|
12273
|
-
const { resetTools:
|
|
12274
|
-
|
|
12273
|
+
const { resetTools: resetTools5 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
|
|
12274
|
+
resetTools5(body.chatId);
|
|
12275
12275
|
jsonResponse(res, { success: true });
|
|
12276
12276
|
} catch (err) {
|
|
12277
12277
|
jsonResponse(res, { error: errorMessage(err) }, 400);
|
|
@@ -12280,8 +12280,8 @@ var init_config = __esm({
|
|
|
12280
12280
|
handleVerboseSet = async (req, res) => {
|
|
12281
12281
|
try {
|
|
12282
12282
|
const body = JSON.parse(await readBody(req));
|
|
12283
|
-
const { setVerboseLevel:
|
|
12284
|
-
|
|
12283
|
+
const { setVerboseLevel: setVerboseLevel5 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
|
|
12284
|
+
setVerboseLevel5(body.chatId, body.level);
|
|
12285
12285
|
jsonResponse(res, { success: true });
|
|
12286
12286
|
} catch (err) {
|
|
12287
12287
|
jsonResponse(res, { error: errorMessage(err) }, 400);
|
|
@@ -12322,8 +12322,8 @@ var init_config = __esm({
|
|
|
12322
12322
|
handleHeartbeatSet = async (req, res) => {
|
|
12323
12323
|
try {
|
|
12324
12324
|
const body = JSON.parse(await readBody(req));
|
|
12325
|
-
const { setHeartbeatConfig:
|
|
12326
|
-
|
|
12325
|
+
const { setHeartbeatConfig: setHeartbeatConfig3 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
|
|
12326
|
+
setHeartbeatConfig3(body.chatId, body);
|
|
12327
12327
|
jsonResponse(res, { success: true });
|
|
12328
12328
|
} catch (err) {
|
|
12329
12329
|
jsonResponse(res, { error: errorMessage(err) }, 400);
|
|
@@ -12383,8 +12383,8 @@ var init_config = __esm({
|
|
|
12383
12383
|
const db3 = getDb();
|
|
12384
12384
|
db3.prepare("INSERT OR REPLACE INTO chat_voice (chat_id, enabled) VALUES (?, ?)").run(body.chatId, body.value === "on" || body.value === "1" ? 1 : 0);
|
|
12385
12385
|
} else if (body.key === "response-style") {
|
|
12386
|
-
const { setResponseStyle:
|
|
12387
|
-
|
|
12386
|
+
const { setResponseStyle: setResponseStyle5 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
|
|
12387
|
+
setResponseStyle5(body.chatId, body.value);
|
|
12388
12388
|
} else if (body.key === "backend") {
|
|
12389
12389
|
const { setBackend: setBackend4, clearSession: clearSession3, clearModel: clearModel4, clearThinkingLevel: clearThinkingLevel4 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
|
|
12390
12390
|
clearSession3(body.chatId);
|
|
@@ -12392,19 +12392,19 @@ var init_config = __esm({
|
|
|
12392
12392
|
clearThinkingLevel4(body.chatId);
|
|
12393
12393
|
setBackend4(body.chatId, body.value);
|
|
12394
12394
|
} else if (body.key === "model") {
|
|
12395
|
-
const { setModel:
|
|
12396
|
-
|
|
12395
|
+
const { setModel: setModel5, clearThinkingLevel: clearThinkingLevel4, clearSession: clearSession3 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
|
|
12396
|
+
setModel5(body.chatId, body.value);
|
|
12397
12397
|
clearThinkingLevel4(body.chatId);
|
|
12398
12398
|
clearSession3(body.chatId);
|
|
12399
12399
|
} else if (body.key === "thinking") {
|
|
12400
|
-
const { setThinkingLevel:
|
|
12401
|
-
|
|
12400
|
+
const { setThinkingLevel: setThinkingLevel5 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
|
|
12401
|
+
setThinkingLevel5(body.chatId, body.value);
|
|
12402
12402
|
} else if (body.key === "mode") {
|
|
12403
|
-
const { setMode:
|
|
12404
|
-
|
|
12403
|
+
const { setMode: setMode5 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
|
|
12404
|
+
setMode5(body.chatId, body.value);
|
|
12405
12405
|
} else if (body.key === "verbose") {
|
|
12406
|
-
const { setVerboseLevel:
|
|
12407
|
-
|
|
12406
|
+
const { setVerboseLevel: setVerboseLevel5 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
|
|
12407
|
+
setVerboseLevel5(body.chatId, body.value);
|
|
12408
12408
|
} else if (body.key === "summarizer") {
|
|
12409
12409
|
const { setSummarizer: setSummarizer4, clearSummarizer: clearSummarizer4 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
|
|
12410
12410
|
if (body.value === "auto") clearSummarizer4(body.chatId);
|
|
@@ -15185,7 +15185,7 @@ async function askAgentImpl(chatId, userMessage, opts) {
|
|
|
15185
15185
|
const slotSpawnOpts = (() => {
|
|
15186
15186
|
if (adapter.id !== "gemini") return spawnOpts;
|
|
15187
15187
|
const geminiAdapter = adapter;
|
|
15188
|
-
const { env, slot } = geminiAdapter.resolveSlotEnv(
|
|
15188
|
+
const { env, slot } = geminiAdapter.resolveSlotEnv(settingsChat);
|
|
15189
15189
|
if (slot) {
|
|
15190
15190
|
log(`[agent] rotation=off, using pinned slot: ${slot.label || `slot-${slot.id}`} (${slot.slotType})`);
|
|
15191
15191
|
return { ...spawnOpts, envOverride: env };
|
|
@@ -15239,7 +15239,7 @@ async function askAgentImpl(chatId, userMessage, opts) {
|
|
|
15239
15239
|
const retryOpts = (() => {
|
|
15240
15240
|
if (adapter.id !== "gemini") return spawnOpts;
|
|
15241
15241
|
const geminiAdapter = adapter;
|
|
15242
|
-
const { env, slot } = geminiAdapter.resolveSlotEnv(
|
|
15242
|
+
const { env, slot } = geminiAdapter.resolveSlotEnv(settingsChat);
|
|
15243
15243
|
if (slot) return { ...spawnOpts, envOverride: env };
|
|
15244
15244
|
return spawnOpts;
|
|
15245
15245
|
})();
|
|
@@ -15262,7 +15262,7 @@ async function askAgentImpl(chatId, userMessage, opts) {
|
|
|
15262
15262
|
const retryOpts = (() => {
|
|
15263
15263
|
if (adapter.id !== "gemini") return spawnOpts;
|
|
15264
15264
|
const geminiAdapter = adapter;
|
|
15265
|
-
const { env, slot } = geminiAdapter.resolveSlotEnv(
|
|
15265
|
+
const { env, slot } = geminiAdapter.resolveSlotEnv(settingsChat);
|
|
15266
15266
|
if (slot) return { ...spawnOpts, envOverride: env };
|
|
15267
15267
|
return spawnOpts;
|
|
15268
15268
|
})();
|
|
@@ -15282,7 +15282,7 @@ async function askAgentImpl(chatId, userMessage, opts) {
|
|
|
15282
15282
|
const retryOpts = (() => {
|
|
15283
15283
|
if (adapter.id !== "gemini") return spawnOpts;
|
|
15284
15284
|
const geminiAdapter = adapter;
|
|
15285
|
-
const { env, slot } = geminiAdapter.resolveSlotEnv(
|
|
15285
|
+
const { env, slot } = geminiAdapter.resolveSlotEnv(settingsChat);
|
|
15286
15286
|
if (slot) return { ...spawnOpts, envOverride: env };
|
|
15287
15287
|
return spawnOpts;
|
|
15288
15288
|
})();
|
|
@@ -15303,7 +15303,7 @@ async function askAgentImpl(chatId, userMessage, opts) {
|
|
|
15303
15303
|
const retryOpts = (() => {
|
|
15304
15304
|
if (adapter.id !== "gemini") return spawnOpts;
|
|
15305
15305
|
const geminiAdapter = adapter;
|
|
15306
|
-
const { env, slot } = geminiAdapter.resolveSlotEnv(
|
|
15306
|
+
const { env, slot } = geminiAdapter.resolveSlotEnv(settingsChat);
|
|
15307
15307
|
if (slot) return { ...spawnOpts, envOverride: env };
|
|
15308
15308
|
return spawnOpts;
|
|
15309
15309
|
})();
|
|
@@ -16374,18 +16374,23 @@ function enableHeartbeat(chatId, opts) {
|
|
|
16374
16374
|
resumeJob2(existing.id);
|
|
16375
16375
|
log(`[heartbeat] Resumed job #${existing.id}`);
|
|
16376
16376
|
}
|
|
16377
|
+
if (!existing.backend) {
|
|
16378
|
+
migrateHeartbeatDefaults(existing);
|
|
16379
|
+
}
|
|
16377
16380
|
return existing.id;
|
|
16378
16381
|
}
|
|
16379
16382
|
const { insertJob: insertJob2 } = (init_jobs(), __toCommonJS(jobs_exports));
|
|
16380
16383
|
const { startSingleJob: startSingleJob2 } = (init_cron(), __toCommonJS(cron_exports));
|
|
16381
16384
|
const intervalMs = opts?.intervalMs ?? DEFAULT_INTERVAL_MS;
|
|
16385
|
+
const backend2 = opts?.backend ?? resolveDefaultBackend(chatId);
|
|
16386
|
+
const model2 = opts?.model ?? resolveDefaultModel(backend2);
|
|
16382
16387
|
const job = insertJob2({
|
|
16383
16388
|
scheduleType: "every",
|
|
16384
16389
|
everyMs: intervalMs,
|
|
16385
16390
|
description: DEFAULT_DESCRIPTION,
|
|
16386
16391
|
chatId,
|
|
16387
|
-
backend:
|
|
16388
|
-
model:
|
|
16392
|
+
backend: backend2,
|
|
16393
|
+
model: model2,
|
|
16389
16394
|
sessionType: "isolated",
|
|
16390
16395
|
deliveryMode: "announce",
|
|
16391
16396
|
channel: opts?.channel ?? "telegram",
|
|
@@ -16395,9 +16400,36 @@ function enableHeartbeat(chatId, opts) {
|
|
|
16395
16400
|
timeout: 120
|
|
16396
16401
|
});
|
|
16397
16402
|
startSingleJob2(job);
|
|
16398
|
-
log(`[heartbeat] Created job #${job.id} (every ${intervalMs / 6e4}min)`);
|
|
16403
|
+
log(`[heartbeat] Created job #${job.id} (every ${intervalMs / 6e4}min, backend=${backend2}, model=${model2})`);
|
|
16399
16404
|
return job.id;
|
|
16400
16405
|
}
|
|
16406
|
+
function resolveDefaultBackend(chatId) {
|
|
16407
|
+
try {
|
|
16408
|
+
const { getAdapterForChat: getAdapterForChat2 } = (init_backends(), __toCommonJS(backends_exports));
|
|
16409
|
+
return getAdapterForChat2(chatId).id;
|
|
16410
|
+
} catch {
|
|
16411
|
+
return "claude";
|
|
16412
|
+
}
|
|
16413
|
+
}
|
|
16414
|
+
function resolveDefaultModel(backend2) {
|
|
16415
|
+
try {
|
|
16416
|
+
const { getAdapter: getAdapter4 } = (init_backends(), __toCommonJS(backends_exports));
|
|
16417
|
+
return getAdapter4(backend2).defaultModel;
|
|
16418
|
+
} catch {
|
|
16419
|
+
return "claude-sonnet-4-6";
|
|
16420
|
+
}
|
|
16421
|
+
}
|
|
16422
|
+
function migrateHeartbeatDefaults(job) {
|
|
16423
|
+
const backend2 = resolveDefaultBackend(job.chatId);
|
|
16424
|
+
const model2 = resolveDefaultModel(backend2);
|
|
16425
|
+
try {
|
|
16426
|
+
const { getDb: getDb2 } = (init_store5(), __toCommonJS(store_exports5));
|
|
16427
|
+
getDb2().prepare("UPDATE jobs SET backend = ?, model = ? WHERE id = ?").run(backend2, model2, job.id);
|
|
16428
|
+
log(`[heartbeat] Migrated job #${job.id}: backend=${backend2}, model=${model2}`);
|
|
16429
|
+
} catch (err) {
|
|
16430
|
+
log(`[heartbeat] Migration failed for job #${job.id}: ${err}`);
|
|
16431
|
+
}
|
|
16432
|
+
}
|
|
16401
16433
|
function disableHeartbeat() {
|
|
16402
16434
|
const job = findHeartbeatJob();
|
|
16403
16435
|
if (!job) return false;
|
|
@@ -16431,6 +16463,14 @@ function updateHeartbeatConfig(updates) {
|
|
|
16431
16463
|
sets.push("model = ?");
|
|
16432
16464
|
values.push(updates.model);
|
|
16433
16465
|
}
|
|
16466
|
+
if (updates.thinking !== void 0) {
|
|
16467
|
+
sets.push("thinking = ?");
|
|
16468
|
+
values.push(updates.thinking);
|
|
16469
|
+
}
|
|
16470
|
+
if (updates.timeout !== void 0) {
|
|
16471
|
+
sets.push("timeout = ?");
|
|
16472
|
+
values.push(updates.timeout);
|
|
16473
|
+
}
|
|
16434
16474
|
if (updates.target !== void 0) {
|
|
16435
16475
|
sets.push("target = ?");
|
|
16436
16476
|
values.push(updates.target);
|
|
@@ -16439,11 +16479,19 @@ function updateHeartbeatConfig(updates) {
|
|
|
16439
16479
|
sets.push("channel = ?");
|
|
16440
16480
|
values.push(updates.channel);
|
|
16441
16481
|
}
|
|
16482
|
+
if (updates.fallbacks !== void 0) {
|
|
16483
|
+
sets.push("fallbacks = ?");
|
|
16484
|
+
values.push(updates.fallbacks);
|
|
16485
|
+
}
|
|
16486
|
+
if (updates.credentialSlotId !== void 0) {
|
|
16487
|
+
sets.push("credential_slot_id = ?");
|
|
16488
|
+
values.push(updates.credentialSlotId);
|
|
16489
|
+
}
|
|
16442
16490
|
if (sets.length === 0) return false;
|
|
16443
16491
|
values.push(job.id);
|
|
16444
16492
|
db3.prepare(`UPDATE jobs SET ${sets.join(", ")} WHERE id = ?`).run(...values);
|
|
16445
16493
|
if (job.enabled) {
|
|
16446
|
-
const { stopJobTimer: stopJobTimer2, startSingleJob: startSingleJob2
|
|
16494
|
+
const { stopJobTimer: stopJobTimer2, startSingleJob: startSingleJob2 } = (init_cron(), __toCommonJS(cron_exports));
|
|
16447
16495
|
const { getJobById: getJobById3 } = (init_store5(), __toCommonJS(store_exports5));
|
|
16448
16496
|
stopJobTimer2(job.id);
|
|
16449
16497
|
const refreshed = getJobById3(job.id);
|
|
@@ -18695,9 +18743,30 @@ var init_gate = __esm({
|
|
|
18695
18743
|
});
|
|
18696
18744
|
|
|
18697
18745
|
// src/voice/stt.ts
|
|
18746
|
+
var stt_exports = {};
|
|
18747
|
+
__export(stt_exports, {
|
|
18748
|
+
ELEVENLABS_VOICES: () => ELEVENLABS_VOICES,
|
|
18749
|
+
GROK_VOICES: () => GROK_VOICES,
|
|
18750
|
+
LOCAL_WHISPER_MODELS: () => LOCAL_WHISPER_MODELS,
|
|
18751
|
+
MACOS_VOICES: () => MACOS_VOICES,
|
|
18752
|
+
downloadWhisperModel: () => downloadWhisperModel,
|
|
18753
|
+
getSttModel: () => getSttModel,
|
|
18754
|
+
getSttProvider: () => getSttProvider,
|
|
18755
|
+
getVoiceConfig: () => getVoiceConfig,
|
|
18756
|
+
isFfmpegAvailable: () => isFfmpegAvailable,
|
|
18757
|
+
isVoiceEnabled: () => isVoiceEnabled,
|
|
18758
|
+
isWhisperCliAvailable: () => isWhisperCliAvailable,
|
|
18759
|
+
isWhisperModelDownloaded: () => isWhisperModelDownloaded,
|
|
18760
|
+
setSttModel: () => setSttModel,
|
|
18761
|
+
setSttProvider: () => setSttProvider,
|
|
18762
|
+
setVoiceProvider: () => setVoiceProvider,
|
|
18763
|
+
synthesizeSpeech: () => synthesizeSpeech,
|
|
18764
|
+
toggleVoice: () => toggleVoice,
|
|
18765
|
+
transcribeAudio: () => transcribeAudio
|
|
18766
|
+
});
|
|
18698
18767
|
import crypto from "crypto";
|
|
18699
18768
|
import { execFile as execFile2, execFileSync as execFileSync3 } from "child_process";
|
|
18700
|
-
import { readFile as readFile3, unlink as unlink2,
|
|
18769
|
+
import { readFile as readFile3, unlink as unlink2, writeFile } from "fs/promises";
|
|
18701
18770
|
import { existsSync as existsSync22 } from "fs";
|
|
18702
18771
|
import { join as join22 } from "path";
|
|
18703
18772
|
import { promisify as promisify2 } from "util";
|
|
@@ -18769,59 +18838,61 @@ function setSttModel(chatId, model2) {
|
|
|
18769
18838
|
ON CONFLICT(chat_id) DO UPDATE SET stt_model = ?
|
|
18770
18839
|
`).run(chatId, model2, model2);
|
|
18771
18840
|
}
|
|
18772
|
-
function
|
|
18773
|
-
if (
|
|
18841
|
+
function isFfmpegAvailable() {
|
|
18842
|
+
if (ffmpegAvailable !== null) return ffmpegAvailable;
|
|
18774
18843
|
try {
|
|
18775
|
-
execFileSync3("
|
|
18776
|
-
|
|
18844
|
+
execFileSync3("ffmpeg", ["-version"], { stdio: "ignore" });
|
|
18845
|
+
ffmpegAvailable = true;
|
|
18777
18846
|
} catch {
|
|
18778
|
-
|
|
18779
|
-
execFileSync3("whisper", ["--help"], { stdio: "ignore" });
|
|
18780
|
-
whisperCliAvailableCache = true;
|
|
18781
|
-
} catch {
|
|
18782
|
-
whisperCliAvailableCache = false;
|
|
18783
|
-
}
|
|
18847
|
+
ffmpegAvailable = false;
|
|
18784
18848
|
}
|
|
18785
|
-
return
|
|
18849
|
+
return ffmpegAvailable;
|
|
18786
18850
|
}
|
|
18787
|
-
function
|
|
18788
|
-
|
|
18789
|
-
execFileSync3("whisper-cli", ["--help"], { stdio: "ignore" });
|
|
18790
|
-
return "whisper-cli";
|
|
18791
|
-
} catch {
|
|
18792
|
-
}
|
|
18793
|
-
try {
|
|
18794
|
-
execFileSync3("whisper", ["--help"], { stdio: "ignore" });
|
|
18795
|
-
return "whisper";
|
|
18796
|
-
} catch {
|
|
18797
|
-
}
|
|
18798
|
-
return null;
|
|
18799
|
-
}
|
|
18800
|
-
function whisperModelPath(model2) {
|
|
18801
|
-
return join22(WHISPER_MODELS_PATH, `ggml-${model2}.bin`);
|
|
18851
|
+
function isWhisperCliAvailable() {
|
|
18852
|
+
return true;
|
|
18802
18853
|
}
|
|
18803
18854
|
function isWhisperModelDownloaded(model2) {
|
|
18804
|
-
|
|
18855
|
+
const hfId = HF_MODEL_IDS[model2];
|
|
18856
|
+
if (!hfId) return false;
|
|
18857
|
+
const home = process.env.HOME ?? "/tmp";
|
|
18858
|
+
const cacheDir = join22(home, ".cache", "huggingface", "hub", `models--${hfId.replace("/", "--")}`);
|
|
18859
|
+
return existsSync22(cacheDir);
|
|
18805
18860
|
}
|
|
18806
18861
|
async function downloadWhisperModel(model2, onProgress) {
|
|
18807
|
-
await mkdir2(WHISPER_MODELS_PATH, { recursive: true });
|
|
18808
|
-
const dest = whisperModelPath(model2);
|
|
18809
|
-
const url = `https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-${model2}.bin`;
|
|
18810
18862
|
const info = LOCAL_WHISPER_MODELS[model2];
|
|
18863
|
+
const hfId = HF_MODEL_IDS[model2];
|
|
18864
|
+
if (!hfId) throw new Error(`Unknown model: ${model2}`);
|
|
18811
18865
|
onProgress?.(`\u2B07\uFE0F Downloading Whisper model ${model2} (${info.size})...`);
|
|
18812
|
-
log(`[stt] Downloading model ${
|
|
18813
|
-
|
|
18814
|
-
|
|
18815
|
-
|
|
18816
|
-
|
|
18817
|
-
|
|
18866
|
+
log(`[stt] Downloading model ${hfId} via @huggingface/transformers`);
|
|
18867
|
+
try {
|
|
18868
|
+
const { pipeline } = await import("@huggingface/transformers");
|
|
18869
|
+
const transcriber = await pipeline("automatic-speech-recognition", hfId);
|
|
18870
|
+
if (cachedPipeline?.model !== model2) {
|
|
18871
|
+
await cachedPipeline?.transcriber?.dispose?.();
|
|
18872
|
+
}
|
|
18873
|
+
cachedPipeline = { model: model2, transcriber };
|
|
18874
|
+
log(`[stt] Model ${model2} downloaded and cached`);
|
|
18875
|
+
onProgress?.(`\u2705 Model ${model2} downloaded and ready!`);
|
|
18876
|
+
} catch (err) {
|
|
18877
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
18878
|
+
log(`[stt] Model download failed: ${msg}`);
|
|
18879
|
+
throw new Error(`Failed to download model ${model2}: ${msg}`);
|
|
18880
|
+
}
|
|
18818
18881
|
}
|
|
18819
18882
|
async function transcribeWithLocalWhisper(audioBuffer, model2, onProgress) {
|
|
18820
18883
|
ensureFfmpeg();
|
|
18821
|
-
const
|
|
18822
|
-
if (!
|
|
18823
|
-
|
|
18824
|
-
|
|
18884
|
+
const hfId = HF_MODEL_IDS[model2];
|
|
18885
|
+
if (!hfId) throw new Error(`Unknown whisper model: ${model2}`);
|
|
18886
|
+
let transcriber;
|
|
18887
|
+
if (cachedPipeline?.model === model2) {
|
|
18888
|
+
transcriber = cachedPipeline.transcriber;
|
|
18889
|
+
} else {
|
|
18890
|
+
onProgress?.(`\u{1F504} Loading Whisper model ${model2}...`);
|
|
18891
|
+
log(`[stt] Loading pipeline for ${hfId}`);
|
|
18892
|
+
const { pipeline } = await import("@huggingface/transformers");
|
|
18893
|
+
transcriber = await pipeline("automatic-speech-recognition", hfId);
|
|
18894
|
+
if (cachedPipeline) await cachedPipeline.transcriber?.dispose?.();
|
|
18895
|
+
cachedPipeline = { model: model2, transcriber };
|
|
18825
18896
|
}
|
|
18826
18897
|
const id = crypto.randomUUID();
|
|
18827
18898
|
const tmpOgg = `/tmp/cc-claw-stt-${id}.ogg`;
|
|
@@ -18829,16 +18900,26 @@ async function transcribeWithLocalWhisper(audioBuffer, model2, onProgress) {
|
|
|
18829
18900
|
try {
|
|
18830
18901
|
await writeFile(tmpOgg, audioBuffer);
|
|
18831
18902
|
await execFileAsync2("ffmpeg", ["-y", "-i", tmpOgg, "-ar", "16000", "-ac", "1", "-c:a", "pcm_s16le", tmpWav]);
|
|
18832
|
-
const
|
|
18833
|
-
const
|
|
18834
|
-
const
|
|
18835
|
-
|
|
18836
|
-
|
|
18837
|
-
|
|
18838
|
-
|
|
18839
|
-
|
|
18840
|
-
|
|
18841
|
-
|
|
18903
|
+
const wavefileMod = await import("wavefile");
|
|
18904
|
+
const WaveFile = wavefileMod.default?.WaveFile ?? wavefileMod.WaveFile;
|
|
18905
|
+
const wavBuffer = await readFile3(tmpWav);
|
|
18906
|
+
const wav = new WaveFile(wavBuffer);
|
|
18907
|
+
wav.toBitDepth("32f");
|
|
18908
|
+
wav.toSampleRate(16e3);
|
|
18909
|
+
let audioData = wav.getSamples();
|
|
18910
|
+
if (Array.isArray(audioData)) {
|
|
18911
|
+
if (audioData.length > 1) {
|
|
18912
|
+
const SCALING_FACTOR = Math.sqrt(2);
|
|
18913
|
+
for (let i = 0; i < audioData[0].length; ++i) {
|
|
18914
|
+
audioData[0][i] = SCALING_FACTOR * (audioData[0][i] + audioData[1][i]) / 2;
|
|
18915
|
+
}
|
|
18916
|
+
}
|
|
18917
|
+
audioData = audioData[0];
|
|
18918
|
+
}
|
|
18919
|
+
const result = await transcriber(audioData);
|
|
18920
|
+
const transcript = (result?.text ?? "").trim();
|
|
18921
|
+
if (!transcript) {
|
|
18922
|
+
throw new Error("Whisper returned empty transcript. The audio may be too short or silent.");
|
|
18842
18923
|
}
|
|
18843
18924
|
return transcript;
|
|
18844
18925
|
} finally {
|
|
@@ -18986,13 +19067,12 @@ async function macOsTts(text, voice2 = "Samantha") {
|
|
|
18986
19067
|
});
|
|
18987
19068
|
return oggBuffer;
|
|
18988
19069
|
}
|
|
18989
|
-
var execFileAsync2, ffmpegAvailable, LOCAL_WHISPER_MODELS, ELEVENLABS_VOICES, GROK_VOICES, MACOS_VOICES,
|
|
19070
|
+
var execFileAsync2, ffmpegAvailable, LOCAL_WHISPER_MODELS, ELEVENLABS_VOICES, GROK_VOICES, MACOS_VOICES, HF_MODEL_IDS, cachedPipeline;
|
|
18990
19071
|
var init_stt = __esm({
|
|
18991
19072
|
"src/voice/stt.ts"() {
|
|
18992
19073
|
"use strict";
|
|
18993
19074
|
init_log();
|
|
18994
19075
|
init_store5();
|
|
18995
|
-
init_paths();
|
|
18996
19076
|
execFileAsync2 = promisify2(execFile2);
|
|
18997
19077
|
ffmpegAvailable = null;
|
|
18998
19078
|
LOCAL_WHISPER_MODELS = {
|
|
@@ -19019,7 +19099,16 @@ var init_stt = __esm({
|
|
|
19019
19099
|
"Samantha": { name: "Samantha", gender: "F" },
|
|
19020
19100
|
"Albert": { name: "Albert", gender: "M" }
|
|
19021
19101
|
};
|
|
19022
|
-
|
|
19102
|
+
HF_MODEL_IDS = {
|
|
19103
|
+
"tiny.en": "Xenova/whisper-tiny.en",
|
|
19104
|
+
"base.en": "Xenova/whisper-base.en",
|
|
19105
|
+
"small.en": "Xenova/whisper-small.en",
|
|
19106
|
+
"small": "Xenova/whisper-small",
|
|
19107
|
+
"medium.en": "Xenova/whisper-medium.en",
|
|
19108
|
+
"medium": "Xenova/whisper-medium",
|
|
19109
|
+
"large-v3-turbo": "Xenova/whisper-large-v3-turbo"
|
|
19110
|
+
};
|
|
19111
|
+
cachedPipeline = null;
|
|
19023
19112
|
}
|
|
19024
19113
|
});
|
|
19025
19114
|
|
|
@@ -19568,13 +19657,13 @@ var init_video = __esm({
|
|
|
19568
19657
|
|
|
19569
19658
|
// src/router/media.ts
|
|
19570
19659
|
import { join as join24 } from "path";
|
|
19571
|
-
import { mkdir as
|
|
19660
|
+
import { mkdir as mkdir2, writeFile as writeFile3, readdir as readdir4, stat as stat3, unlink as unlink4 } from "fs/promises";
|
|
19572
19661
|
function getMediaRetentionMs() {
|
|
19573
19662
|
const hours = parseInt(process.env.MEDIA_RETENTION_HOURS ?? "24", 10);
|
|
19574
19663
|
return (isNaN(hours) || hours < 1 ? 24 : hours) * 60 * 60 * 1e3;
|
|
19575
19664
|
}
|
|
19576
19665
|
async function saveMedia(buffer, prefix, ext) {
|
|
19577
|
-
await
|
|
19666
|
+
await mkdir2(MEDIA_INCOMING_PATH, { recursive: true });
|
|
19578
19667
|
const filename = `${prefix}-${Date.now()}.${ext}`;
|
|
19579
19668
|
const fullPath = join24(MEDIA_INCOMING_PATH, filename);
|
|
19580
19669
|
await writeFile3(fullPath, buffer);
|
|
@@ -19582,7 +19671,7 @@ async function saveMedia(buffer, prefix, ext) {
|
|
|
19582
19671
|
}
|
|
19583
19672
|
async function cleanupOldMedia() {
|
|
19584
19673
|
try {
|
|
19585
|
-
await
|
|
19674
|
+
await mkdir2(MEDIA_INCOMING_PATH, { recursive: true });
|
|
19586
19675
|
const retentionMs = getMediaRetentionMs();
|
|
19587
19676
|
const retentionHours = Math.round(retentionMs / (60 * 60 * 1e3));
|
|
19588
19677
|
const files = await readdir4(MEDIA_INCOMING_PATH);
|
|
@@ -19652,7 +19741,20 @@ async function handleVoice(msg, channel) {
|
|
|
19652
19741
|
await sendResponse(chatId, channel, response.text, msg.messageId);
|
|
19653
19742
|
} catch (err) {
|
|
19654
19743
|
error("[router] Voice error:", err);
|
|
19655
|
-
|
|
19744
|
+
const raw = errorMessage(err);
|
|
19745
|
+
let userMsg;
|
|
19746
|
+
if (raw.includes("ffmpeg")) {
|
|
19747
|
+
userMsg = "\u274C ffmpeg is not installed \u2014 needed to convert audio.\n\nFix: brew install ffmpeg";
|
|
19748
|
+
} else if (raw.includes("GROQ_API_KEY") || raw.includes("Groq API error")) {
|
|
19749
|
+
userMsg = "\u274C Groq transcription failed. Check your GROQ_API_KEY in ~/.cc-claw/.env, or switch to Local Whisper via /voice.";
|
|
19750
|
+
} else if (raw.includes("empty transcript")) {
|
|
19751
|
+
userMsg = "\u274C Couldn't hear anything in that voice message. Try again with a longer or clearer recording.";
|
|
19752
|
+
} else if (raw.includes("Failed to download model")) {
|
|
19753
|
+
userMsg = "\u274C Couldn't download the Whisper model. Check your internet connection and try again via /voice.";
|
|
19754
|
+
} else {
|
|
19755
|
+
userMsg = `\u274C Voice processing error: ${raw}`;
|
|
19756
|
+
}
|
|
19757
|
+
await channel.sendText(chatId, userMsg, { parseMode: "plain" });
|
|
19656
19758
|
}
|
|
19657
19759
|
}
|
|
19658
19760
|
async function handleMedia(msg, channel) {
|
|
@@ -19740,11 +19842,12 @@ Acknowledge receipt. Do NOT analyze the video unless they ask you to.`;
|
|
|
19740
19842
|
return;
|
|
19741
19843
|
}
|
|
19742
19844
|
const fileBuffer = await channel.downloadFile(fileName);
|
|
19743
|
-
const originalName = fileName ?? "file";
|
|
19845
|
+
const originalName = msg.metadata?.originalName ?? fileName ?? "file";
|
|
19744
19846
|
const ext = originalName.split(".").pop()?.toLowerCase() ?? "";
|
|
19847
|
+
const effectiveExt = ext && ext !== originalName ? ext : msg.mimeType?.split("/")[1]?.replace("plain", "txt").replace("javascript", "js") ?? "";
|
|
19745
19848
|
let prompt;
|
|
19746
19849
|
let tempFilePath;
|
|
19747
|
-
if (msg.type === "photo" || isImageExt(ext)) {
|
|
19850
|
+
if (msg.type === "photo" || isImageExt(ext) || isImageExt(effectiveExt)) {
|
|
19748
19851
|
const imgExt = msg.type === "photo" ? "jpg" : ext || "jpg";
|
|
19749
19852
|
tempFilePath = await saveMedia(fileBuffer, "img", imgExt);
|
|
19750
19853
|
const allPaths = [tempFilePath];
|
|
@@ -19780,7 +19883,7 @@ ${pathList}
|
|
|
19780
19883
|
|
|
19781
19884
|
Please read all image files and describe what you see.`;
|
|
19782
19885
|
}
|
|
19783
|
-
} else if (isTextExt(ext)) {
|
|
19886
|
+
} else if (isTextExt(ext) || isTextExt(effectiveExt)) {
|
|
19784
19887
|
const MAX_TEXT_FILE_BYTES = 1048576;
|
|
19785
19888
|
if (fileBuffer.length > MAX_TEXT_FILE_BYTES) {
|
|
19786
19889
|
await channel.sendText(chatId, `\u26A0\uFE0F Text file too large for inline processing (${(fileBuffer.length / 1024 / 1024).toFixed(1)}MB, max 1MB). Saving to disk instead.`, { parseMode: "plain" });
|
|
@@ -19805,7 +19908,25 @@ ${content}
|
|
|
19805
19908
|
\`\`\``;
|
|
19806
19909
|
}
|
|
19807
19910
|
} else {
|
|
19808
|
-
|
|
19911
|
+
const MAX_TEXT_SNIFF = 1048576;
|
|
19912
|
+
const looksLikeText = fileBuffer.length <= MAX_TEXT_SNIFF && !fileBuffer.includes(0);
|
|
19913
|
+
if (looksLikeText) {
|
|
19914
|
+
const content = fileBuffer.toString("utf-8");
|
|
19915
|
+
prompt = caption ? `Here's a file "${originalName}":
|
|
19916
|
+
|
|
19917
|
+
\`\`\`
|
|
19918
|
+
${content}
|
|
19919
|
+
\`\`\`
|
|
19920
|
+
|
|
19921
|
+
${caption}` : `Here's a file "${originalName}". Please analyze its contents:
|
|
19922
|
+
|
|
19923
|
+
\`\`\`
|
|
19924
|
+
${content}
|
|
19925
|
+
\`\`\``;
|
|
19926
|
+
} else {
|
|
19927
|
+
tempFilePath = await saveMedia(fileBuffer, "file", ext || "bin");
|
|
19928
|
+
prompt = `The user shared a binary file "${originalName}" (${fileBuffer.length} bytes), saved at: ${tempFilePath}. ${caption || "What can you tell me about it?"}`;
|
|
19929
|
+
}
|
|
19809
19930
|
}
|
|
19810
19931
|
const mediaModel = resolveModel(chatId);
|
|
19811
19932
|
const mMode = getMode(chatId);
|
|
@@ -20179,7 +20300,7 @@ var install_exports = {};
|
|
|
20179
20300
|
__export(install_exports, {
|
|
20180
20301
|
installSkillFromGitHub: () => installSkillFromGitHub
|
|
20181
20302
|
});
|
|
20182
|
-
import { mkdir as
|
|
20303
|
+
import { mkdir as mkdir3, readdir as readdir5, readFile as readFile5, cp } from "fs/promises";
|
|
20183
20304
|
import { existsSync as existsSync24 } from "fs";
|
|
20184
20305
|
import { join as join25, basename as basename2 } from "path";
|
|
20185
20306
|
import { execSync as execSync4 } from "child_process";
|
|
@@ -20212,7 +20333,7 @@ async function installSkillFromGitHub(urlOrShorthand) {
|
|
|
20212
20333
|
if (existsSync24(destDir)) {
|
|
20213
20334
|
log(`[skill-install] Overwriting existing skill at ${destDir}`);
|
|
20214
20335
|
}
|
|
20215
|
-
await
|
|
20336
|
+
await mkdir3(destDir, { recursive: true });
|
|
20216
20337
|
await cp(skillDir, destDir, { recursive: true });
|
|
20217
20338
|
let skillName = skillFolderName;
|
|
20218
20339
|
try {
|
|
@@ -20423,6 +20544,53 @@ var init_humanize = __esm({
|
|
|
20423
20544
|
});
|
|
20424
20545
|
|
|
20425
20546
|
// src/router/ui.ts
|
|
20547
|
+
var ui_exports = {};
|
|
20548
|
+
__export(ui_exports, {
|
|
20549
|
+
ROTATION_MODE_CONFIRM_LABELS: () => ROTATION_MODE_CONFIRM_LABELS,
|
|
20550
|
+
ROTATION_MODE_LABELS: () => ROTATION_MODE_LABELS,
|
|
20551
|
+
buildAccountSlotKeyboard: () => buildAccountSlotKeyboard,
|
|
20552
|
+
doBackendSwitch: () => doBackendSwitch,
|
|
20553
|
+
getJobScheduleText: () => getJobScheduleText,
|
|
20554
|
+
getJobStatusEmoji: () => getJobStatusEmoji,
|
|
20555
|
+
getJobStatusLabel: () => getJobStatusLabel,
|
|
20556
|
+
handleRotationModeChange: () => handleRotationModeChange,
|
|
20557
|
+
sendBackendAccountPicker: () => sendBackendAccountPicker,
|
|
20558
|
+
sendBackendConfigPanel: () => sendBackendConfigPanel,
|
|
20559
|
+
sendBackendModelPicker: () => sendBackendModelPicker,
|
|
20560
|
+
sendBackendPicker: () => sendBackendPicker,
|
|
20561
|
+
sendBackendSwitchConfirmation: () => sendBackendSwitchConfirmation,
|
|
20562
|
+
sendBackendThinkingPicker: () => sendBackendThinkingPicker,
|
|
20563
|
+
sendCurrentProposal: () => sendCurrentProposal,
|
|
20564
|
+
sendForgetPicker: () => sendForgetPicker,
|
|
20565
|
+
sendHeartbeatEngine: () => sendHeartbeatEngine,
|
|
20566
|
+
sendHeartbeatFallbacks: () => sendHeartbeatFallbacks,
|
|
20567
|
+
sendHeartbeatHours: () => sendHeartbeatHours,
|
|
20568
|
+
sendHeartbeatKeyboard: () => sendHeartbeatKeyboard,
|
|
20569
|
+
sendHeartbeatThinking: () => sendHeartbeatThinking,
|
|
20570
|
+
sendHeartbeatTimeout: () => sendHeartbeatTimeout,
|
|
20571
|
+
sendHeartbeatWatches: () => sendHeartbeatWatches,
|
|
20572
|
+
sendHelpCategories: () => sendHelpCategories,
|
|
20573
|
+
sendHelpCategory: () => sendHelpCategory,
|
|
20574
|
+
sendHistoryView: () => sendHistoryView,
|
|
20575
|
+
sendJobDetail: () => sendJobDetail,
|
|
20576
|
+
sendJobPicker: () => sendJobPicker,
|
|
20577
|
+
sendJobRunsView: () => sendJobRunsView,
|
|
20578
|
+
sendJobsBoard: () => sendJobsBoard,
|
|
20579
|
+
sendMemoryDetail: () => sendMemoryDetail,
|
|
20580
|
+
sendMemoryForgetConfirm: () => sendMemoryForgetConfirm,
|
|
20581
|
+
sendMemoryPage: () => sendMemoryPage,
|
|
20582
|
+
sendModelKeyboard: () => sendModelKeyboard,
|
|
20583
|
+
sendModelSigKeyboard: () => sendModelSigKeyboard,
|
|
20584
|
+
sendPermissionsKeyboard: () => sendPermissionsKeyboard,
|
|
20585
|
+
sendResponseStyleKeyboard: () => sendResponseStyleKeyboard,
|
|
20586
|
+
sendSkillsPage: () => sendSkillsPage,
|
|
20587
|
+
sendThinkingKeyboard: () => sendThinkingKeyboard,
|
|
20588
|
+
sendToolsKeyboard: () => sendToolsKeyboard,
|
|
20589
|
+
sendUnifiedUsage: () => sendUnifiedUsage,
|
|
20590
|
+
sendUsageLimits: () => sendUsageLimits,
|
|
20591
|
+
sendVerboseKeyboard: () => sendVerboseKeyboard,
|
|
20592
|
+
sendVoiceConfigKeyboard: () => sendVoiceConfigKeyboard
|
|
20593
|
+
});
|
|
20426
20594
|
function buildAccountSlotKeyboard(slots, activeSlotId, rotationMode, callbackPrefix, rotationPrefix) {
|
|
20427
20595
|
const slotButtons = slots.filter((s) => s.enabled).map((s) => {
|
|
20428
20596
|
const label2 = s.label || `slot-${s.id}`;
|
|
@@ -20470,8 +20638,7 @@ async function handleRotationModeChange(chatId, channel, backendId, mode) {
|
|
|
20470
20638
|
const prefix = isGemini ? "" : `${displayName} `;
|
|
20471
20639
|
await channel.sendText(chatId, `${prefix}Rotation mode set to <b>${ROTATION_MODE_CONFIRM_LABELS[mode]}</b>.`, { parseMode: "html" });
|
|
20472
20640
|
}
|
|
20473
|
-
async function sendHelpCategories(chatId, channel) {
|
|
20474
|
-
if (typeof channel.sendKeyboard !== "function") return;
|
|
20641
|
+
async function sendHelpCategories(chatId, channel, messageId) {
|
|
20475
20642
|
const header2 = `${buildSectionHeader("CC-Claw Commands")}`;
|
|
20476
20643
|
const cats = Object.keys(HELP_CATEGORIES);
|
|
20477
20644
|
const rows = [];
|
|
@@ -20484,22 +20651,21 @@ async function sendHelpCategories(chatId, channel) {
|
|
|
20484
20651
|
}
|
|
20485
20652
|
rows.push(row);
|
|
20486
20653
|
}
|
|
20487
|
-
await
|
|
20654
|
+
await sendOrEditKeyboard(chatId, channel, messageId, header2, rows);
|
|
20488
20655
|
}
|
|
20489
|
-
async function sendHelpCategory(chatId, category, channel) {
|
|
20490
|
-
if (typeof channel.sendKeyboard !== "function") return;
|
|
20656
|
+
async function sendHelpCategory(chatId, category, channel, messageId) {
|
|
20491
20657
|
const cat = Object.entries(HELP_CATEGORIES).find(([k]) => k.toLowerCase() === category.toLowerCase());
|
|
20492
20658
|
if (!cat) return;
|
|
20493
20659
|
const [name, cmds] = cat;
|
|
20494
20660
|
const header2 = `${buildSectionHeader(`${name} (${cmds.length} commands)`)}`;
|
|
20495
20661
|
const body = cmds.map((c) => `${c.cmd} \u2014 ${c.desc}`).join("\n");
|
|
20496
|
-
await
|
|
20662
|
+
await sendOrEditKeyboard(chatId, channel, messageId, `${header2}
|
|
20497
20663
|
|
|
20498
20664
|
${body}`, [
|
|
20499
20665
|
[{ label: "\u2190 Back to Categories", data: "help:back" }]
|
|
20500
20666
|
]);
|
|
20501
20667
|
}
|
|
20502
|
-
async function sendUnifiedUsage(chatId, channel, view) {
|
|
20668
|
+
async function sendUnifiedUsage(chatId, channel, view, messageId) {
|
|
20503
20669
|
const { pricing, defaultPricing } = buildUsagePricing();
|
|
20504
20670
|
const fmt = (n) => `$${n.toFixed(4)}`;
|
|
20505
20671
|
const fmtK = (n) => `${(n / 1e3).toFixed(0)}K`;
|
|
@@ -20555,25 +20721,21 @@ async function sendUnifiedUsage(chatId, channel, view) {
|
|
|
20555
20721
|
}
|
|
20556
20722
|
}
|
|
20557
20723
|
}
|
|
20558
|
-
|
|
20559
|
-
|
|
20560
|
-
|
|
20561
|
-
|
|
20562
|
-
|
|
20563
|
-
|
|
20564
|
-
|
|
20565
|
-
|
|
20566
|
-
|
|
20567
|
-
|
|
20568
|
-
|
|
20569
|
-
|
|
20570
|
-
|
|
20571
|
-
await channel.sendKeyboard(chatId, lines.join("\n"), buttons);
|
|
20572
|
-
} else {
|
|
20573
|
-
await channel.sendText(chatId, lines.join("\n"), { parseMode: "plain" });
|
|
20574
|
-
}
|
|
20724
|
+
const buttons = [
|
|
20725
|
+
[
|
|
20726
|
+
{ label: view === "session" ? "\u2713 Session" : "Session", data: "usage:session", ...view === "session" ? { style: "primary" } : {} },
|
|
20727
|
+
{ label: view === "24h" ? "\u2713 24h" : "24h", data: "usage:24h", ...view === "24h" ? { style: "primary" } : {} },
|
|
20728
|
+
{ label: view === "7d" ? "\u2713 7d" : "7d", data: "usage:7d", ...view === "7d" ? { style: "primary" } : {} },
|
|
20729
|
+
{ label: view === "30d" ? "\u2713 30d" : "30d", data: "usage:30d", ...view === "30d" ? { style: "primary" } : {} }
|
|
20730
|
+
],
|
|
20731
|
+
[
|
|
20732
|
+
{ label: view === "model" ? "\u2713 By Model" : "By Model", data: "usage:model", ...view === "model" ? { style: "primary" } : {} },
|
|
20733
|
+
{ label: "Set Limits", data: "usage:limits" }
|
|
20734
|
+
]
|
|
20735
|
+
];
|
|
20736
|
+
await sendOrEditKeyboard(chatId, channel, messageId, lines.join("\n"), buttons);
|
|
20575
20737
|
}
|
|
20576
|
-
async function sendUsageLimits(chatId, channel) {
|
|
20738
|
+
async function sendUsageLimits(chatId, channel, messageId) {
|
|
20577
20739
|
const limits2 = getAllBackendLimits();
|
|
20578
20740
|
const fmtK = (n) => `${(n / 1e3).toFixed(0)}K`;
|
|
20579
20741
|
const lines = [buildSectionHeader("Set Usage Limits"), ""];
|
|
@@ -20587,27 +20749,19 @@ async function sendUsageLimits(chatId, channel) {
|
|
|
20587
20749
|
}
|
|
20588
20750
|
}
|
|
20589
20751
|
lines.push("", "Use /limits <backend> <window> <tokens> to set.\nExample: /limits claude daily 500000");
|
|
20590
|
-
|
|
20591
|
-
|
|
20592
|
-
|
|
20593
|
-
|
|
20594
|
-
|
|
20595
|
-
|
|
20596
|
-
|
|
20597
|
-
rows.push(setButtons.slice(i, i + 2));
|
|
20598
|
-
}
|
|
20599
|
-
rows.push([
|
|
20600
|
-
{ label: "Clear All", data: "usage:limits:clear", style: "danger" }
|
|
20601
|
-
]);
|
|
20602
|
-
rows.push([
|
|
20603
|
-
{ label: "\u2190 Back to Usage", data: "usage:back" }
|
|
20604
|
-
]);
|
|
20605
|
-
await channel.sendKeyboard(chatId, lines.join("\n"), rows);
|
|
20606
|
-
} else {
|
|
20607
|
-
await channel.sendText(chatId, lines.join("\n"), { parseMode: "plain" });
|
|
20752
|
+
const setButtons = getAvailableBackendIds().map((bid) => ({
|
|
20753
|
+
label: `Set ${getAdapter(bid).displayName} Limit`,
|
|
20754
|
+
data: `usage:limits:set:${bid}`
|
|
20755
|
+
}));
|
|
20756
|
+
const rows = [];
|
|
20757
|
+
for (let i = 0; i < setButtons.length; i += 2) {
|
|
20758
|
+
rows.push(setButtons.slice(i, i + 2));
|
|
20608
20759
|
}
|
|
20760
|
+
rows.push([{ label: "Clear All", data: "usage:limits:clear", style: "danger" }]);
|
|
20761
|
+
rows.push([{ label: "\u2190 Back to Usage", data: "usage:back" }]);
|
|
20762
|
+
await sendOrEditKeyboard(chatId, channel, messageId, lines.join("\n"), rows);
|
|
20609
20763
|
}
|
|
20610
|
-
async function sendHistoryView(chatId, channel, filter) {
|
|
20764
|
+
async function sendHistoryView(chatId, channel, filter, messageId) {
|
|
20611
20765
|
const limit = filter.limit ?? 10;
|
|
20612
20766
|
let rows = getRecentMessageLog(chatId, 100).reverse();
|
|
20613
20767
|
if (filter.backend) {
|
|
@@ -20622,7 +20776,9 @@ async function sendHistoryView(chatId, channel, filter) {
|
|
|
20622
20776
|
}
|
|
20623
20777
|
const userMsgs = rows.filter((r) => r.role === "user").slice(0, limit);
|
|
20624
20778
|
if (userMsgs.length === 0) {
|
|
20625
|
-
await
|
|
20779
|
+
await sendOrEditKeyboard(chatId, channel, messageId, "No conversation history found.", [
|
|
20780
|
+
[[{ label: "Show More (25)", data: "hist:recent:25" }, { label: "Today", data: "hist:today" }, { label: "This Week", data: "hist:week" }]][0]
|
|
20781
|
+
]);
|
|
20626
20782
|
return;
|
|
20627
20783
|
}
|
|
20628
20784
|
const lines = [`\u{1F4CB} Recent Conversation (last ${userMsgs.length})`, "\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"];
|
|
@@ -20631,28 +20787,28 @@ async function sendHistoryView(chatId, channel, filter) {
|
|
|
20631
20787
|
const preview = row.content.slice(0, 80) + (row.content.length > 80 ? "\u2026" : "");
|
|
20632
20788
|
lines.push(`[${row.backend ?? "?"} \xB7 ${date}] ${preview}`);
|
|
20633
20789
|
}
|
|
20634
|
-
|
|
20790
|
+
lines.push("", "Tip: /history <query> for full-text search");
|
|
20635
20791
|
const filterRow = [
|
|
20636
|
-
{ label: "Show More (25)", data: "hist:recent:25" },
|
|
20637
|
-
{ label: "Today", data: "hist:today" },
|
|
20638
|
-
{ label: "This Week", data: "hist:week" }
|
|
20792
|
+
{ label: filter.limit === 25 ? "\u2713 Show More (25)" : "Show More (25)", data: "hist:recent:25", ...filter.limit === 25 ? { style: "primary" } : {} },
|
|
20793
|
+
{ label: filter.period === "today" ? "\u2713 Today" : "Today", data: "hist:today", ...filter.period === "today" ? { style: "primary" } : {} },
|
|
20794
|
+
{ label: filter.period === "week" ? "\u2713 This Week" : "This Week", data: "hist:week", ...filter.period === "week" ? { style: "primary" } : {} }
|
|
20639
20795
|
];
|
|
20640
20796
|
const backendRow = getAvailableAdapters().map((a) => ({
|
|
20641
|
-
label: a.displayName,
|
|
20797
|
+
label: filter.backend === a.id ? `\u2713 ${a.displayName}` : a.displayName,
|
|
20642
20798
|
data: `hist:backend:${a.id}`,
|
|
20643
|
-
style: "primary"
|
|
20799
|
+
...filter.backend === a.id ? { style: "primary" } : {}
|
|
20644
20800
|
}));
|
|
20645
|
-
await
|
|
20801
|
+
await sendOrEditKeyboard(chatId, channel, messageId, lines.join("\n"), [filterRow, backendRow]);
|
|
20646
20802
|
}
|
|
20647
|
-
async function sendVoiceConfigKeyboard(chatId, channel) {
|
|
20648
|
-
if (typeof channel.sendKeyboard !== "function") {
|
|
20803
|
+
async function sendVoiceConfigKeyboard(chatId, channel, messageId) {
|
|
20804
|
+
if (typeof channel.sendKeyboard !== "function" && typeof channel.editKeyboard !== "function") {
|
|
20649
20805
|
await channel.sendText(chatId, "Voice configuration requires an interactive channel (Telegram).", { parseMode: "plain" });
|
|
20650
20806
|
return;
|
|
20651
20807
|
}
|
|
20652
20808
|
const ttsConfig = getVoiceConfig(chatId);
|
|
20653
20809
|
const sttProvider = getSttProvider(chatId);
|
|
20654
20810
|
const sttModel = getSttModel(chatId);
|
|
20655
|
-
const
|
|
20811
|
+
const ffmpegOk = isFfmpegAvailable();
|
|
20656
20812
|
const buttons = [];
|
|
20657
20813
|
const groqAvailable = !!process.env.GROQ_API_KEY;
|
|
20658
20814
|
buttons.push([
|
|
@@ -20662,12 +20818,12 @@ async function sendVoiceConfigKeyboard(chatId, channel) {
|
|
|
20662
20818
|
...sttProvider === "groq" ? { style: "primary" } : {}
|
|
20663
20819
|
},
|
|
20664
20820
|
{
|
|
20665
|
-
label: `${sttProvider === "local-whisper" ? "\u2713 " : ""}\u{1F4BB} Local Whisper
|
|
20821
|
+
label: `${sttProvider === "local-whisper" ? "\u2713 " : ""}\u{1F4BB} Local Whisper`,
|
|
20666
20822
|
data: "vcfg:stt:local-whisper",
|
|
20667
20823
|
...sttProvider === "local-whisper" ? { style: "primary" } : {}
|
|
20668
20824
|
}
|
|
20669
20825
|
]);
|
|
20670
|
-
if (sttProvider === "local-whisper"
|
|
20826
|
+
if (sttProvider === "local-whisper") {
|
|
20671
20827
|
const modelEntries = Object.entries(LOCAL_WHISPER_MODELS);
|
|
20672
20828
|
for (let i = 0; i < modelEntries.length; i += 2) {
|
|
20673
20829
|
const row = modelEntries.slice(i, i + 2).map(([id, info]) => {
|
|
@@ -20712,14 +20868,158 @@ async function sendVoiceConfigKeyboard(chatId, channel) {
|
|
|
20712
20868
|
buttons.push(Object.entries(MACOS_VOICES).map(([id, v]) => ({ label: `${ttsConfig.voiceId === id ? "\u2713 " : ""}${v.name}`, data: `vcfg:v:${id}` })));
|
|
20713
20869
|
}
|
|
20714
20870
|
}
|
|
20715
|
-
const sttLabel = sttProvider === "groq" ? "Groq (cloud)" : `Local Whisper \xB7 ${sttModel}`;
|
|
20871
|
+
const sttLabel = sttProvider === "groq" ? "Groq (cloud)" : `Local Whisper \xB7 ${sttModel} (on-device)`;
|
|
20716
20872
|
const ttsLabel = ttsConfig.enabled ? `${ttsConfig.provider === "grok" ? "Grok" : ttsConfig.provider === "macos" ? "macOS" : "ElevenLabs"} replies ON` : "Replies OFF";
|
|
20717
|
-
const modelLegend = sttProvider === "local-whisper"
|
|
20873
|
+
const modelLegend = sttProvider === "local-whisper" ? "\n\u25CF = downloaded \u25CB = tap to download" : "";
|
|
20874
|
+
const prereqWarning = sttProvider === "local-whisper" && !ffmpegOk ? "\n\u26A0\uFE0F ffmpeg not found \u2014 required for audio conversion" : "";
|
|
20718
20875
|
const header2 = `\u{1F399}\uFE0F Voice Settings
|
|
20719
20876
|
|
|
20720
20877
|
\u{1F3A4} Transcription: ${sttLabel}
|
|
20721
|
-
\u{1F50A} Text-to-Speech: ${ttsLabel}${modelLegend}`;
|
|
20722
|
-
await
|
|
20878
|
+
\u{1F50A} Text-to-Speech: ${ttsLabel}${modelLegend}${prereqWarning}`;
|
|
20879
|
+
await sendOrEditKeyboard(chatId, channel, messageId, header2, buttons);
|
|
20880
|
+
}
|
|
20881
|
+
async function sendPermissionsKeyboard(chatId, channel, messageId) {
|
|
20882
|
+
const currentMode = getMode(chatId);
|
|
20883
|
+
const currentExecMode = getExecMode(chatId);
|
|
20884
|
+
const permButtons = Object.entries(PERM_MODES).map(([id, label2]) => [{
|
|
20885
|
+
label: `${id === currentMode ? "\u2713 " : ""}${label2}`,
|
|
20886
|
+
data: `perms:${id}`,
|
|
20887
|
+
...id === currentMode ? { style: "primary" } : {}
|
|
20888
|
+
}]);
|
|
20889
|
+
const approvalOn = currentExecMode === "approved";
|
|
20890
|
+
permButtons.push([{
|
|
20891
|
+
label: `${approvalOn ? "\u2713 " : ""}\u{1F512} Approve Before Execute: ${approvalOn ? "ON" : "OFF"}`,
|
|
20892
|
+
data: `execmode:${approvalOn ? "yolo" : "approved"}`,
|
|
20893
|
+
...approvalOn ? { style: "primary" } : {}
|
|
20894
|
+
}]);
|
|
20895
|
+
await sendOrEditKeyboard(chatId, channel, messageId, "\u2699\uFE0F Permission & Execution Settings:", permButtons);
|
|
20896
|
+
}
|
|
20897
|
+
async function sendVerboseKeyboard(chatId, channel, messageId) {
|
|
20898
|
+
const currentVerbose = getVerboseLevel(chatId);
|
|
20899
|
+
const buttons = Object.entries(VERBOSE_LEVELS).map(([id, label2]) => [{
|
|
20900
|
+
label: `${id === currentVerbose ? "\u2713 " : ""}${label2}`,
|
|
20901
|
+
data: `verbose:${id}`,
|
|
20902
|
+
...id === currentVerbose ? { style: "primary" } : {}
|
|
20903
|
+
}]);
|
|
20904
|
+
await sendOrEditKeyboard(chatId, channel, messageId, "\u{1F441}\uFE0F Tool Visibility:", buttons);
|
|
20905
|
+
}
|
|
20906
|
+
async function sendToolsKeyboard(chatId, channel, messageId) {
|
|
20907
|
+
const toolsMap = getToolsMap(chatId);
|
|
20908
|
+
const currentMode = getMode(chatId);
|
|
20909
|
+
const toolButtons = [];
|
|
20910
|
+
const toolList = [...ALL_TOOLS];
|
|
20911
|
+
for (let i = 0; i < toolList.length; i += 3) {
|
|
20912
|
+
const row = toolList.slice(i, i + 3).map((t) => ({
|
|
20913
|
+
label: `${toolsMap[t] ? "\u2705" : "\u274C"} ${t}`,
|
|
20914
|
+
data: `tool:toggle:${t}`
|
|
20915
|
+
}));
|
|
20916
|
+
toolButtons.push(row);
|
|
20917
|
+
}
|
|
20918
|
+
toolButtons.push([{ label: "Reset to defaults (all on)", data: "tool:reset", style: "danger" }]);
|
|
20919
|
+
const enabledCount = Object.values(toolsMap).filter(Boolean).length;
|
|
20920
|
+
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)." : "";
|
|
20921
|
+
await sendOrEditKeyboard(
|
|
20922
|
+
chatId,
|
|
20923
|
+
channel,
|
|
20924
|
+
messageId,
|
|
20925
|
+
`\u{1F527} Tools (${enabledCount}/${toolList.length} enabled, mode: ${currentMode})${modeNote}
|
|
20926
|
+
Tap to toggle:`,
|
|
20927
|
+
toolButtons
|
|
20928
|
+
);
|
|
20929
|
+
}
|
|
20930
|
+
async function sendResponseStyleKeyboard(chatId, channel, messageId) {
|
|
20931
|
+
const currentStyle = getResponseStyle(chatId);
|
|
20932
|
+
await sendOrEditKeyboard(chatId, channel, messageId, "\u{1F5E3}\uFE0F AI Response Style:", [
|
|
20933
|
+
[
|
|
20934
|
+
{ label: `${currentStyle === "concise" ? "\u2713 " : ""}Concise`, data: "style:concise", ...currentStyle === "concise" ? { style: "primary" } : {} },
|
|
20935
|
+
{ label: `${currentStyle === "normal" ? "\u2713 " : ""}Normal`, data: "style:normal", ...currentStyle === "normal" ? { style: "primary" } : {} },
|
|
20936
|
+
{ label: `${currentStyle === "detailed" ? "\u2713 " : ""}Detailed`, data: "style:detailed", ...currentStyle === "detailed" ? { style: "primary" } : {} }
|
|
20937
|
+
]
|
|
20938
|
+
]);
|
|
20939
|
+
}
|
|
20940
|
+
async function sendModelSigKeyboard(chatId, channel, messageId) {
|
|
20941
|
+
const currentSig = getModelSignature(chatId);
|
|
20942
|
+
await sendOrEditKeyboard(
|
|
20943
|
+
chatId,
|
|
20944
|
+
channel,
|
|
20945
|
+
messageId,
|
|
20946
|
+
`\u{1F9E0} Model Signature: ${currentSig === "on" ? "ON" : "OFF"}
|
|
20947
|
+
Appends model + thinking level to each response.`,
|
|
20948
|
+
[[
|
|
20949
|
+
{ label: `${currentSig === "on" ? "\u2713 " : ""}On`, data: "model_sig:on", ...currentSig === "on" ? { style: "success" } : {} },
|
|
20950
|
+
{ label: `${currentSig !== "on" ? "\u2713 " : ""}Off`, data: "model_sig:off", ...currentSig !== "on" ? { style: "danger" } : {} }
|
|
20951
|
+
]]
|
|
20952
|
+
);
|
|
20953
|
+
}
|
|
20954
|
+
async function sendModelKeyboard(chatId, channel, messageId) {
|
|
20955
|
+
let adapter;
|
|
20956
|
+
try {
|
|
20957
|
+
adapter = getAdapterForChat(chatId);
|
|
20958
|
+
} catch {
|
|
20959
|
+
await channel.sendText(chatId, "No backend set. Use /backend first.", { parseMode: "plain" });
|
|
20960
|
+
return;
|
|
20961
|
+
}
|
|
20962
|
+
const models = adapter.availableModels;
|
|
20963
|
+
const current = getModel(chatId) ?? adapter.defaultModel;
|
|
20964
|
+
const isAuto = getModel(chatId) === "auto";
|
|
20965
|
+
const buttons = Object.entries(models).map(([id, info]) => {
|
|
20966
|
+
const tag = info.thinking === "adjustable" ? " \u26A1" : "";
|
|
20967
|
+
return [{
|
|
20968
|
+
label: `${id === current ? "\u2713 " : ""}${info.label}${tag}`,
|
|
20969
|
+
data: `model:${id}`,
|
|
20970
|
+
...id === current ? { style: "primary" } : {}
|
|
20971
|
+
}];
|
|
20972
|
+
});
|
|
20973
|
+
buttons.unshift([{
|
|
20974
|
+
label: `${isAuto ? "\u2713 " : ""}\u{1F916} Auto (smart routing)`,
|
|
20975
|
+
data: "model:auto",
|
|
20976
|
+
...isAuto ? { style: "primary" } : {}
|
|
20977
|
+
}]);
|
|
20978
|
+
await sendOrEditKeyboard(chatId, channel, messageId, `Models for ${adapter.displayName}:`, buttons);
|
|
20979
|
+
}
|
|
20980
|
+
async function sendThinkingKeyboard(chatId, channel, messageId, forModelId) {
|
|
20981
|
+
let adapter;
|
|
20982
|
+
try {
|
|
20983
|
+
adapter = getAdapterForChat(chatId);
|
|
20984
|
+
} catch {
|
|
20985
|
+
await channel.sendText(chatId, "No backend set. Use /backend first.", { parseMode: "plain" });
|
|
20986
|
+
return;
|
|
20987
|
+
}
|
|
20988
|
+
const currentModel = forModelId ?? getModel(chatId) ?? adapter.defaultModel;
|
|
20989
|
+
const modelInfo = adapter.availableModels[currentModel];
|
|
20990
|
+
const currentLevel = getThinkingLevel(chatId) || "auto";
|
|
20991
|
+
if (!modelInfo || modelInfo.thinking !== "adjustable" || !modelInfo.thinkingLevels) {
|
|
20992
|
+
await sendOrEditKeyboard(
|
|
20993
|
+
chatId,
|
|
20994
|
+
channel,
|
|
20995
|
+
messageId,
|
|
20996
|
+
`Model ${shortModelName(currentModel)} uses fixed thinking \u2014 no adjustment needed.`,
|
|
20997
|
+
[[{ label: "\u2190 Back to Model", data: "menu:model" }]]
|
|
20998
|
+
);
|
|
20999
|
+
return;
|
|
21000
|
+
}
|
|
21001
|
+
const showThinkingUi = getShowThinkingUi(chatId);
|
|
21002
|
+
const buttons = modelInfo.thinkingLevels.map((level) => [{
|
|
21003
|
+
label: `${level === currentLevel ? "\u2713 " : ""}${level === "auto" ? "Auto" : capitalize(level)}`,
|
|
21004
|
+
data: `thinking:${level}`,
|
|
21005
|
+
...level === currentLevel ? { style: "primary" } : {}
|
|
21006
|
+
}]);
|
|
21007
|
+
buttons.push([{
|
|
21008
|
+
label: `${showThinkingUi ? "\u2713 " : ""}\u{1F4AD} Show Thinking`,
|
|
21009
|
+
data: "thinking_show_ui:toggle",
|
|
21010
|
+
...showThinkingUi ? { style: "primary" } : {}
|
|
21011
|
+
}]);
|
|
21012
|
+
await sendOrEditKeyboard(
|
|
21013
|
+
chatId,
|
|
21014
|
+
channel,
|
|
21015
|
+
messageId,
|
|
21016
|
+
`\u{1F4AD} Thinking Level \u2014 ${shortModelName(currentModel)}
|
|
21017
|
+
Current: ${capitalize(currentLevel)}
|
|
21018
|
+
Show thinking tokens: ${showThinkingUi ? "On" : "Off"}${adapter.id !== "claude" ? `
|
|
21019
|
+
|
|
21020
|
+
\u26A0\uFE0F ${adapter.displayName} CLI doesn't stream thinking tokens` : ""}`,
|
|
21021
|
+
buttons
|
|
21022
|
+
);
|
|
20723
21023
|
}
|
|
20724
21024
|
async function sendSkillsPage(chatId, channel, skills2, page, messageId) {
|
|
20725
21025
|
const approved = skills2.filter((s) => s.status === "approved");
|
|
@@ -20805,69 +21105,135 @@ async function sendMemoryPage(chatId, channel, page, messageId) {
|
|
|
20805
21105
|
await channel.sendText(chatId, "No memories stored yet.", { parseMode: "plain" });
|
|
20806
21106
|
return;
|
|
20807
21107
|
}
|
|
20808
|
-
const pageSize =
|
|
21108
|
+
const pageSize = 8;
|
|
20809
21109
|
const totalPages = Math.max(1, Math.ceil(memories.length / pageSize));
|
|
20810
21110
|
const safePage = Math.max(1, Math.min(page, totalPages));
|
|
20811
21111
|
const start = (safePage - 1) * pageSize;
|
|
20812
21112
|
const pageItems = memories.slice(start, start + pageSize);
|
|
20813
|
-
const
|
|
20814
|
-
`Memories (${memories.length} total, ${safePage === 1 ? "top " : ""}${pageItems.length} by relevance${totalPages > 1 ? ` \xB7 page ${safePage}/${totalPages}` : ""})`,
|
|
20815
|
-
"\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",
|
|
20816
|
-
""
|
|
20817
|
-
];
|
|
20818
|
-
for (const [i, m] of pageItems.entries()) {
|
|
20819
|
-
lines.push(`${start + i + 1}. ${m.trigger}: ${m.content.slice(0, 60)}${m.content.length > 60 ? "\u2026" : ""}`);
|
|
20820
|
-
}
|
|
21113
|
+
const header2 = `\u{1F9E0} Memories (${memories.length} total${totalPages > 1 ? ` \xB7 page ${safePage}/${totalPages}` : ""})`;
|
|
20821
21114
|
const buttons = [];
|
|
20822
|
-
const itemRow = [];
|
|
20823
21115
|
for (const [i, m] of pageItems.entries()) {
|
|
20824
|
-
|
|
20825
|
-
|
|
20826
|
-
|
|
20827
|
-
|
|
20828
|
-
|
|
21116
|
+
const num = start + i + 1;
|
|
21117
|
+
const preview = `${m.trigger}: ${m.content}`.slice(0, 32);
|
|
21118
|
+
buttons.push([{
|
|
21119
|
+
label: `${num}. ${preview}${m.content.length > 32 ? "\u2026" : ""}`,
|
|
21120
|
+
data: `mem:view:${m.id}`
|
|
21121
|
+
}]);
|
|
20829
21122
|
}
|
|
20830
|
-
if (itemRow.length > 0) buttons.push([...itemRow]);
|
|
20831
21123
|
if (totalPages > 1) {
|
|
20832
21124
|
const navRow = [];
|
|
20833
21125
|
if (safePage > 1) navRow.push({ label: `\u2190 Page ${safePage - 1}`, data: `mem:page:${safePage - 1}` });
|
|
20834
|
-
navRow.push({ label: `${safePage}/${totalPages}`, data: "mem:
|
|
21126
|
+
navRow.push({ label: `${safePage}/${totalPages}`, data: "mem:noop" });
|
|
20835
21127
|
if (safePage < totalPages) navRow.push({ label: `Page ${safePage + 1} \u2192`, data: `mem:page:${safePage + 1}` });
|
|
20836
21128
|
buttons.push(navRow);
|
|
20837
21129
|
}
|
|
20838
|
-
const footerRow = [
|
|
20839
|
-
|
|
20840
|
-
|
|
21130
|
+
const footerRow = [
|
|
21131
|
+
{ label: "\u{1F50D} Search", data: "mem:noop", switchInlineQueryCurrentChat: "" }
|
|
21132
|
+
];
|
|
21133
|
+
if (memories.length > 30) {
|
|
21134
|
+
footerRow.push({ label: "\u{1F4C4} Export All", data: "mem:showall", style: "primary" });
|
|
20841
21135
|
}
|
|
20842
|
-
footerRow.push({ label: "Search: /memory <query>", data: "mem:page:noop" });
|
|
20843
21136
|
buttons.push(footerRow);
|
|
21137
|
+
await sendOrEditKeyboard(chatId, channel, messageId, header2, buttons);
|
|
21138
|
+
}
|
|
21139
|
+
async function sendMemoryDetail(chatId, memoryId, channel, messageId) {
|
|
21140
|
+
const memory2 = getMemoryById(memoryId);
|
|
21141
|
+
if (!memory2) {
|
|
21142
|
+
await sendOrEditKeyboard(chatId, channel, messageId, "This memory no longer exists.", [
|
|
21143
|
+
[{ label: "\u2190 Back to List", data: "mem:back" }]
|
|
21144
|
+
]);
|
|
21145
|
+
return;
|
|
21146
|
+
}
|
|
21147
|
+
const lines = [
|
|
21148
|
+
`\u{1F9E0} Memory #${memory2.id}`,
|
|
21149
|
+
buildSectionHeader("", 26),
|
|
21150
|
+
"",
|
|
21151
|
+
`Trigger: ${memory2.trigger}`,
|
|
21152
|
+
`Content: ${memory2.content}`,
|
|
21153
|
+
"",
|
|
21154
|
+
`Salience: ${memory2.salience.toFixed(2)} \xB7 Created: ${memory2.created_at.slice(0, 10)}`
|
|
21155
|
+
];
|
|
21156
|
+
const buttons = [
|
|
21157
|
+
[
|
|
21158
|
+
{ label: "\u270F\uFE0F Edit", data: `mem:edit:${memoryId}`, style: "primary" },
|
|
21159
|
+
{ label: "\u{1F5D1} Forget", data: `mem:forget:${memoryId}`, style: "danger" }
|
|
21160
|
+
],
|
|
21161
|
+
[{ label: "\u2190 Back to List", data: "mem:back" }]
|
|
21162
|
+
];
|
|
20844
21163
|
await sendOrEditKeyboard(chatId, channel, messageId, lines.join("\n"), buttons);
|
|
20845
21164
|
}
|
|
20846
|
-
async function
|
|
21165
|
+
async function sendMemoryForgetConfirm(chatId, memoryId, channel, messageId) {
|
|
21166
|
+
const memory2 = getMemoryById(memoryId);
|
|
21167
|
+
if (!memory2) {
|
|
21168
|
+
await sendOrEditKeyboard(chatId, channel, messageId, "Memory already deleted.", [
|
|
21169
|
+
[{ label: "\u2190 Back to List", data: "mem:back" }]
|
|
21170
|
+
]);
|
|
21171
|
+
return;
|
|
21172
|
+
}
|
|
21173
|
+
const lines = [
|
|
21174
|
+
"Delete this memory?",
|
|
21175
|
+
"",
|
|
21176
|
+
`"${memory2.trigger}: ${memory2.content}"`
|
|
21177
|
+
];
|
|
21178
|
+
const buttons = [
|
|
21179
|
+
[
|
|
21180
|
+
{ label: "\u2705 Confirm Delete", data: `mem:forget:confirm:${memoryId}`, style: "danger" },
|
|
21181
|
+
{ label: "Cancel", data: `mem:view:${memoryId}` }
|
|
21182
|
+
]
|
|
21183
|
+
];
|
|
21184
|
+
await sendOrEditKeyboard(chatId, channel, messageId, lines.join("\n"), buttons);
|
|
21185
|
+
}
|
|
21186
|
+
function getHeartbeatContext(chatId) {
|
|
21187
|
+
const { findHeartbeatJob: findHeartbeatJob2 } = (init_heartbeat2(), __toCommonJS(heartbeat_exports));
|
|
21188
|
+
const job = findHeartbeatJob2();
|
|
20847
21189
|
const config2 = getHeartbeatConfig(chatId);
|
|
20848
|
-
const enabled = config2?.enabled === 1;
|
|
20849
|
-
const intervalMs = config2?.intervalMs ?? 18e5;
|
|
20850
|
-
const intervalMin = intervalMs / 6e4;
|
|
20851
|
-
const activeStart = config2?.activeStart ?? "08:00";
|
|
20852
|
-
const activeEnd = config2?.activeEnd ?? "22:00";
|
|
20853
21190
|
const watches = getActiveWatches(chatId);
|
|
20854
|
-
|
|
20855
|
-
|
|
20856
|
-
|
|
20857
|
-
|
|
20858
|
-
|
|
20859
|
-
|
|
20860
|
-
|
|
20861
|
-
|
|
20862
|
-
|
|
20863
|
-
|
|
20864
|
-
|
|
20865
|
-
|
|
21191
|
+
return {
|
|
21192
|
+
job,
|
|
21193
|
+
activeStart: config2?.activeStart ?? "08:00",
|
|
21194
|
+
activeEnd: config2?.activeEnd ?? "22:00",
|
|
21195
|
+
lastBeatAt: config2?.lastBeatAt ?? job?.lastRunAt ?? null,
|
|
21196
|
+
watches
|
|
21197
|
+
};
|
|
21198
|
+
}
|
|
21199
|
+
function formatEngine(job) {
|
|
21200
|
+
if (!job) return "Not configured";
|
|
21201
|
+
const backend2 = job.backend ?? "not set";
|
|
21202
|
+
let modelName = job.model ?? "not set";
|
|
21203
|
+
if (job.model) {
|
|
21204
|
+
try {
|
|
21205
|
+
modelName = shortModelName(job.model);
|
|
21206
|
+
} catch {
|
|
21207
|
+
}
|
|
21208
|
+
}
|
|
21209
|
+
return `${capitalize(backend2)} \xB7 ${modelName}`;
|
|
21210
|
+
}
|
|
21211
|
+
async function sendHeartbeatKeyboard(chatId, channel, messageId) {
|
|
21212
|
+
const { job, activeStart, activeEnd, lastBeatAt, watches } = getHeartbeatContext(chatId);
|
|
21213
|
+
const enabled = job ? !!job.enabled : false;
|
|
21214
|
+
const intervalMin = job ? (job.everyMs ?? 18e5) / 6e4 : 30;
|
|
21215
|
+
const fallbacks = job?.fallbacks ?? [];
|
|
21216
|
+
const slotLabel = job?.credentialSlotId ? resolveSlotLabel(job.backend ?? void 0, job.credentialSlotId) : null;
|
|
21217
|
+
const lastBeat = lastBeatAt ? String(lastBeatAt).replace("T", " ").slice(0, 16) : "never";
|
|
21218
|
+
const lines = [
|
|
21219
|
+
"\u{1FAC0} Heartbeat",
|
|
21220
|
+
buildSectionHeader("", 26),
|
|
21221
|
+
"",
|
|
21222
|
+
`Status: ${enabled ? "ON" : "OFF"} \xB7 Every ${intervalMin} min`,
|
|
21223
|
+
`Hours: ${activeStart}\u2013${activeEnd}`,
|
|
21224
|
+
`Engine: ${formatEngine(job)}`
|
|
21225
|
+
];
|
|
21226
|
+
if (slotLabel) lines.push(`Account: ${slotLabel}`);
|
|
21227
|
+
if (fallbacks.length > 0) {
|
|
21228
|
+
lines.push(`Fallbacks: ${fallbacks.map((f) => `${capitalize(f.backend)}${f.model ? ` (${shortModelName(f.model)})` : ""}`).join(" \u2192 ")}`);
|
|
21229
|
+
}
|
|
21230
|
+
if (job?.thinking && job.thinking !== "auto") lines.push(`Thinking: ${job.thinking}`);
|
|
21231
|
+
if (job?.timeout && job.timeout !== 120) lines.push(`Timeout: ${job.timeout}s`);
|
|
21232
|
+
lines.push(`Last run: ${lastBeat}`);
|
|
20866
21233
|
const buttons = [];
|
|
20867
21234
|
buttons.push([
|
|
20868
21235
|
{ label: `${enabled ? "\u2713 " : ""}On`, data: "hb:on", ...enabled ? { style: "success" } : {} },
|
|
20869
|
-
{ label: `${!enabled ? "\u2713 " : ""}Off`, data: "hb:off", ...!enabled ? { style: "danger" } : {} }
|
|
20870
|
-
{ label: "\u25B6 Run Now", data: "hb:run", style: "primary" }
|
|
21236
|
+
{ label: `${!enabled ? "\u2713 " : ""}Off`, data: "hb:off", ...!enabled ? { style: "danger" } : {} }
|
|
20871
21237
|
]);
|
|
20872
21238
|
const presets = [15, 30, 60, 120];
|
|
20873
21239
|
buttons.push(presets.map((m) => ({
|
|
@@ -20875,63 +21241,225 @@ async function sendHeartbeatKeyboard(chatId, channel, messageId) {
|
|
|
20875
21241
|
data: `hb:interval:${m}`,
|
|
20876
21242
|
...m === intervalMin ? { style: "primary" } : {}
|
|
20877
21243
|
})));
|
|
20878
|
-
|
|
20879
|
-
|
|
20880
|
-
{
|
|
20881
|
-
|
|
20882
|
-
|
|
20883
|
-
|
|
20884
|
-
}
|
|
20885
|
-
|
|
20886
|
-
|
|
20887
|
-
|
|
20888
|
-
|
|
20889
|
-
}
|
|
21244
|
+
buttons.push([
|
|
21245
|
+
{ label: `\u23F0 Active Hours`, data: "hb:hours" },
|
|
21246
|
+
{ label: "\u25B6 Run Now", data: "hb:run", style: "primary" }
|
|
21247
|
+
]);
|
|
21248
|
+
buttons.push([
|
|
21249
|
+
{ label: "\u{1F9E0} Engine & Account", data: "hb:engine" },
|
|
21250
|
+
{ label: `\u{1F517} Fallbacks${fallbacks.length > 0 ? ` (${fallbacks.length})` : ""}`, data: "hb:fallbacks" }
|
|
21251
|
+
]);
|
|
21252
|
+
const row5 = [
|
|
21253
|
+
{ label: `\u{1F441} Watches${watches.length > 0 ? ` (${watches.length})` : ""}`, data: "hb:watches" },
|
|
21254
|
+
{ label: "\u23F1 Timeout", data: "hb:timeout" },
|
|
21255
|
+
{ label: "\u{1F4AD} Thinking", data: "hb:thinking" }
|
|
20890
21256
|
];
|
|
20891
|
-
buttons.push(
|
|
20892
|
-
|
|
20893
|
-
|
|
20894
|
-
|
|
20895
|
-
|
|
20896
|
-
|
|
20897
|
-
|
|
20898
|
-
|
|
20899
|
-
|
|
20900
|
-
|
|
20901
|
-
|
|
20902
|
-
|
|
20903
|
-
|
|
20904
|
-
|
|
21257
|
+
buttons.push(row5);
|
|
21258
|
+
await sendOrEditKeyboard(chatId, channel, messageId, lines.join("\n"), buttons);
|
|
21259
|
+
}
|
|
21260
|
+
async function sendHeartbeatEngine(chatId, channel, messageId) {
|
|
21261
|
+
const { job } = getHeartbeatContext(chatId);
|
|
21262
|
+
const currentBackend = job?.backend ?? null;
|
|
21263
|
+
const currentModel = job?.model ?? null;
|
|
21264
|
+
const slotLabel = job?.credentialSlotId ? resolveSlotLabel(currentBackend ?? void 0, job.credentialSlotId) : "Auto (rotate)";
|
|
21265
|
+
const lines = [
|
|
21266
|
+
"\u{1FAC0} Heartbeat \u203A Engine & Account",
|
|
21267
|
+
buildSectionHeader("", 26),
|
|
21268
|
+
"",
|
|
21269
|
+
`Backend: ${currentBackend ? capitalize(currentBackend) : "\u26A0\uFE0F Not set"}`,
|
|
21270
|
+
`Model: ${currentModel ? shortModelName(currentModel) : "\u26A0\uFE0F Not set"}`,
|
|
21271
|
+
`Account: ${slotLabel}`
|
|
21272
|
+
];
|
|
21273
|
+
const buttons = [];
|
|
21274
|
+
const available = getAvailableBackendIds();
|
|
21275
|
+
const backendBtns = available.map((bid) => ({
|
|
21276
|
+
label: `${currentBackend === bid ? "\u2713 " : ""}${capitalize(bid)}`,
|
|
21277
|
+
data: `hb:backend:${bid}`,
|
|
21278
|
+
...currentBackend === bid ? { style: "primary" } : {}
|
|
21279
|
+
}));
|
|
21280
|
+
for (let i = 0; i < backendBtns.length; i += 3) {
|
|
21281
|
+
buttons.push(backendBtns.slice(i, i + 3));
|
|
21282
|
+
}
|
|
21283
|
+
if (currentBackend) {
|
|
21284
|
+
try {
|
|
21285
|
+
const adapter = getAdapter(currentBackend);
|
|
21286
|
+
const models = Object.entries(adapter.availableModels);
|
|
21287
|
+
if (models.length > 0) {
|
|
21288
|
+
buttons.push([{ label: "\u2500\u2500 Model \u2500\u2500", data: "hb:noop" }]);
|
|
21289
|
+
const modelBtns = models.map(([key]) => ({
|
|
21290
|
+
label: `${currentModel === key ? "\u2713 " : ""}${shortModelName(key)}`,
|
|
20905
21291
|
data: `hb:model:${key}`,
|
|
20906
|
-
...
|
|
20907
|
-
}))
|
|
20908
|
-
|
|
20909
|
-
|
|
21292
|
+
...currentModel === key ? { style: "primary" } : {}
|
|
21293
|
+
}));
|
|
21294
|
+
for (let i = 0; i < modelBtns.length; i += 3) {
|
|
21295
|
+
buttons.push(modelBtns.slice(i, i + 3));
|
|
21296
|
+
}
|
|
21297
|
+
}
|
|
21298
|
+
} catch {
|
|
20910
21299
|
}
|
|
20911
|
-
|
|
21300
|
+
const isGemini = currentBackend === "gemini";
|
|
21301
|
+
const slots = isGemini ? getGeminiSlots() : getBackendSlots(currentBackend);
|
|
21302
|
+
const enabledSlots = slots.filter((s) => s.enabled !== 0);
|
|
21303
|
+
if (enabledSlots.length > 0) {
|
|
21304
|
+
buttons.push([{ label: "\u2500\u2500 Account \u2500\u2500", data: "hb:noop" }]);
|
|
21305
|
+
buttons.push([{
|
|
21306
|
+
label: `${!job?.credentialSlotId ? "\u2713 " : ""}Auto (rotate)`,
|
|
21307
|
+
data: "hb:slot:auto",
|
|
21308
|
+
...!job?.credentialSlotId ? { style: "primary" } : {}
|
|
21309
|
+
}]);
|
|
21310
|
+
for (const s of enabledSlots) {
|
|
21311
|
+
const icon = s.slotType === "api_key" ? "\u{1F511}" : "\u{1F4E7}";
|
|
21312
|
+
const label2 = s.label || s.email || `Slot #${s.id}`;
|
|
21313
|
+
buttons.push([{
|
|
21314
|
+
label: `${job?.credentialSlotId === s.id ? "\u2713 " : ""}${icon} ${label2}`,
|
|
21315
|
+
data: `hb:slot:${s.id}`,
|
|
21316
|
+
...job?.credentialSlotId === s.id ? { style: "primary" } : {}
|
|
21317
|
+
}]);
|
|
21318
|
+
}
|
|
21319
|
+
}
|
|
21320
|
+
} else {
|
|
21321
|
+
buttons.push([{ label: "\u26A0\uFE0F Pick a backend first", data: "hb:noop" }]);
|
|
20912
21322
|
}
|
|
20913
|
-
|
|
21323
|
+
buttons.push([{ label: "\u2190 Back", data: "hb:back" }]);
|
|
21324
|
+
await sendOrEditKeyboard(chatId, channel, messageId, lines.join("\n"), buttons);
|
|
21325
|
+
}
|
|
21326
|
+
async function sendHeartbeatFallbacks(chatId, channel, messageId) {
|
|
21327
|
+
const { job } = getHeartbeatContext(chatId);
|
|
21328
|
+
const fallbacks = job?.fallbacks ?? [];
|
|
21329
|
+
const currentBackend = job?.backend ?? null;
|
|
21330
|
+
const lines = [
|
|
21331
|
+
"\u{1FAC0} Heartbeat \u203A Fallbacks",
|
|
21332
|
+
buildSectionHeader("", 26),
|
|
21333
|
+
"",
|
|
21334
|
+
"If the primary backend is unavailable,",
|
|
21335
|
+
"these fire in order:",
|
|
21336
|
+
""
|
|
21337
|
+
];
|
|
20914
21338
|
if (fallbacks.length > 0) {
|
|
20915
|
-
|
|
20916
|
-
|
|
20917
|
-
|
|
20918
|
-
|
|
21339
|
+
for (let i = 0; i < fallbacks.length; i++) {
|
|
21340
|
+
const f = fallbacks[i];
|
|
21341
|
+
const model2 = f.model ? ` (${shortModelName(f.model)})` : "";
|
|
21342
|
+
lines.push(` ${i + 1}. ${capitalize(f.backend)}${model2}`);
|
|
21343
|
+
}
|
|
21344
|
+
} else {
|
|
21345
|
+
lines.push(" (none configured)");
|
|
20919
21346
|
}
|
|
20920
|
-
|
|
20921
|
-
|
|
20922
|
-
|
|
21347
|
+
const buttons = [];
|
|
21348
|
+
const available = getAvailableBackendIds();
|
|
21349
|
+
const used = new Set([currentBackend, ...fallbacks.map((f) => f.backend)].filter(Boolean));
|
|
21350
|
+
const candidates = available.filter((bid) => !used.has(bid));
|
|
21351
|
+
if (candidates.length > 0) {
|
|
21352
|
+
const addBtns = candidates.map((bid) => ({
|
|
21353
|
+
label: `+ ${capitalize(bid)}`,
|
|
20923
21354
|
data: `hb:fb:add:${bid}`
|
|
20924
|
-
}))
|
|
21355
|
+
}));
|
|
21356
|
+
for (let i = 0; i < addBtns.length; i += 3) {
|
|
21357
|
+
buttons.push(addBtns.slice(i, i + 3));
|
|
21358
|
+
}
|
|
20925
21359
|
}
|
|
20926
|
-
|
|
21360
|
+
if (fallbacks.length > 0) {
|
|
21361
|
+
buttons.push([{ label: "\u2715 Clear All", data: "hb:fb:clear", style: "danger" }]);
|
|
21362
|
+
}
|
|
21363
|
+
buttons.push([{ label: "\u2190 Back", data: "hb:back" }]);
|
|
21364
|
+
await sendOrEditKeyboard(chatId, channel, messageId, lines.join("\n"), buttons);
|
|
21365
|
+
}
|
|
21366
|
+
async function sendHeartbeatHours(chatId, channel, messageId) {
|
|
21367
|
+
const config2 = getHeartbeatConfig(chatId);
|
|
21368
|
+
const activeStart = config2?.activeStart ?? "08:00";
|
|
21369
|
+
const activeEnd = config2?.activeEnd ?? "22:00";
|
|
21370
|
+
const lines = [
|
|
21371
|
+
"\u{1FAC0} Heartbeat \u203A Active Hours",
|
|
21372
|
+
buildSectionHeader("", 26),
|
|
21373
|
+
"",
|
|
21374
|
+
`Current: ${activeStart}\u2013${activeEnd}`,
|
|
21375
|
+
"Heartbeat only runs during these hours."
|
|
21376
|
+
];
|
|
21377
|
+
const startHours = ["06:00", "07:00", "08:00", "09:00", "10:00"];
|
|
21378
|
+
const endHours = ["20:00", "21:00", "22:00", "23:00", "00:00"];
|
|
21379
|
+
const buttons = [];
|
|
21380
|
+
buttons.push([{ label: "\u2500\u2500 Start \u2500\u2500", data: "hb:noop" }]);
|
|
21381
|
+
buttons.push(startHours.map((h) => ({
|
|
21382
|
+
label: `${h === activeStart ? "\u2713 " : ""}${h}`,
|
|
21383
|
+
data: `hb:hstart:${h}`,
|
|
21384
|
+
...h === activeStart ? { style: "primary" } : {}
|
|
21385
|
+
})));
|
|
21386
|
+
buttons.push([{ label: "\u2500\u2500 End \u2500\u2500", data: "hb:noop" }]);
|
|
21387
|
+
buttons.push(endHours.map((h) => ({
|
|
21388
|
+
label: `${h === activeEnd ? "\u2713 " : ""}${h}`,
|
|
21389
|
+
data: `hb:hend:${h}`,
|
|
21390
|
+
...h === activeEnd ? { style: "primary" } : {}
|
|
21391
|
+
})));
|
|
21392
|
+
buttons.push([{ label: "\u2190 Back", data: "hb:back" }]);
|
|
21393
|
+
await sendOrEditKeyboard(chatId, channel, messageId, lines.join("\n"), buttons);
|
|
21394
|
+
}
|
|
21395
|
+
async function sendHeartbeatTimeout(chatId, channel, messageId) {
|
|
21396
|
+
const { job } = getHeartbeatContext(chatId);
|
|
21397
|
+
const current = job?.timeout ?? 120;
|
|
21398
|
+
const lines = [
|
|
21399
|
+
"\u{1FAC0} Heartbeat \u203A Timeout",
|
|
21400
|
+
buildSectionHeader("", 26),
|
|
21401
|
+
"",
|
|
21402
|
+
`Current: ${current}s (${Math.round(current / 60)} min)`,
|
|
21403
|
+
"Max time per heartbeat check."
|
|
21404
|
+
];
|
|
21405
|
+
const presets = [60, 120, 300, 600];
|
|
21406
|
+
const buttons = [];
|
|
21407
|
+
buttons.push(presets.map((s) => ({
|
|
21408
|
+
label: `${s === current ? "\u2713 " : ""}${s >= 60 ? `${Math.round(s / 60)} min` : `${s}s`}`,
|
|
21409
|
+
data: `hb:tout:${s}`,
|
|
21410
|
+
...s === current ? { style: "primary" } : {}
|
|
21411
|
+
})));
|
|
21412
|
+
buttons.push([{ label: "\u2190 Back", data: "hb:back" }]);
|
|
21413
|
+
await sendOrEditKeyboard(chatId, channel, messageId, lines.join("\n"), buttons);
|
|
21414
|
+
}
|
|
21415
|
+
async function sendHeartbeatThinking(chatId, channel, messageId) {
|
|
21416
|
+
const { job } = getHeartbeatContext(chatId);
|
|
21417
|
+
const current = job?.thinking ?? "auto";
|
|
21418
|
+
const lines = [
|
|
21419
|
+
"\u{1FAC0} Heartbeat \u203A Thinking Level",
|
|
21420
|
+
buildSectionHeader("", 26),
|
|
21421
|
+
"",
|
|
21422
|
+
`Current: ${current}`,
|
|
21423
|
+
"Reasoning depth for heartbeat checks."
|
|
21424
|
+
];
|
|
21425
|
+
const levels = ["auto", "off", "low", "medium", "high"];
|
|
21426
|
+
const buttons = [];
|
|
21427
|
+
buttons.push(levels.slice(0, 3).map((l) => ({
|
|
21428
|
+
label: `${l === current ? "\u2713 " : ""}${capitalize(l)}`,
|
|
21429
|
+
data: `hb:think:${l}`,
|
|
21430
|
+
...l === current ? { style: "primary" } : {}
|
|
21431
|
+
})));
|
|
21432
|
+
buttons.push(levels.slice(3).map((l) => ({
|
|
21433
|
+
label: `${l === current ? "\u2713 " : ""}${capitalize(l)}`,
|
|
21434
|
+
data: `hb:think:${l}`,
|
|
21435
|
+
...l === current ? { style: "primary" } : {}
|
|
21436
|
+
})));
|
|
21437
|
+
buttons.push([{ label: "\u2190 Back", data: "hb:back" }]);
|
|
21438
|
+
await sendOrEditKeyboard(chatId, channel, messageId, lines.join("\n"), buttons);
|
|
21439
|
+
}
|
|
21440
|
+
async function sendHeartbeatWatches(chatId, channel, messageId) {
|
|
21441
|
+
const watches = getActiveWatches(chatId);
|
|
21442
|
+
const lines = [
|
|
21443
|
+
"\u{1FAC0} Heartbeat \u203A Watches",
|
|
21444
|
+
buildSectionHeader("", 26),
|
|
21445
|
+
""
|
|
21446
|
+
];
|
|
20927
21447
|
if (watches.length > 0) {
|
|
20928
|
-
|
|
21448
|
+
for (const w of watches) {
|
|
21449
|
+
const expiry = w.expiresAt ? ` (until ${w.expiresAt.slice(0, 16)})` : "";
|
|
21450
|
+
lines.push(` ${w.id}. ${w.description}${expiry}`);
|
|
21451
|
+
}
|
|
21452
|
+
lines.push("");
|
|
21453
|
+
lines.push("Remove: /watch remove <id>");
|
|
20929
21454
|
} else {
|
|
20930
|
-
|
|
21455
|
+
lines.push(" (no active watches)");
|
|
20931
21456
|
}
|
|
20932
|
-
|
|
20933
|
-
|
|
20934
|
-
|
|
21457
|
+
lines.push("");
|
|
21458
|
+
lines.push("Add: /watch add <description>");
|
|
21459
|
+
lines.push("Example: /watch add Check email for urgent messages");
|
|
21460
|
+
const buttons = [];
|
|
21461
|
+
buttons.push([{ label: "\u2190 Back", data: "hb:back" }]);
|
|
21462
|
+
await sendOrEditKeyboard(chatId, channel, messageId, lines.join("\n"), buttons);
|
|
20935
21463
|
}
|
|
20936
21464
|
async function sendForgetPicker(chatId, channel, page, messageId) {
|
|
20937
21465
|
const memories = listMemories();
|
|
@@ -21159,94 +21687,223 @@ async function sendJobPicker(chatId, channel, action) {
|
|
|
21159
21687
|
buttons.push([{ label: "Cancel", data: "job:back" }]);
|
|
21160
21688
|
await channel.sendKeyboard(chatId, `${actionLabel} which job?`, buttons);
|
|
21161
21689
|
}
|
|
21162
|
-
async function sendCurrentProposal(chatId, channel) {
|
|
21163
|
-
const { getReviewSession: getReviewSession2, getInsightById: getInsightById2, deleteReviewSession: deleteReviewSession2 } = await Promise.resolve().then(() => (init_store4(), store_exports4));
|
|
21690
|
+
async function sendCurrentProposal(chatId, channel, messageId) {
|
|
21691
|
+
const { getReviewSession: getReviewSession2, getInsightById: getInsightById2, deleteReviewSession: deleteReviewSession2, advanceReviewSession: advanceReviewSession2 } = await Promise.resolve().then(() => (init_store4(), store_exports4));
|
|
21164
21692
|
const { formatProposalCardWithProgress: formatProposalCardWithProgress2, buildProposalKeyboard: buildProposalKeyboard2, buildReviewCompleteMessage: buildReviewCompleteMessage3 } = await Promise.resolve().then(() => (init_propose(), propose_exports));
|
|
21165
21693
|
const session2 = getReviewSession2(getDb(), chatId);
|
|
21166
21694
|
if (!session2) return;
|
|
21167
21695
|
if (session2.currentIndex >= session2.insightIds.length) {
|
|
21168
21696
|
const summary = buildReviewCompleteMessage3(session2.results);
|
|
21169
21697
|
deleteReviewSession2(getDb(), chatId);
|
|
21170
|
-
await
|
|
21698
|
+
await sendOrEditKeyboard(
|
|
21699
|
+
chatId,
|
|
21700
|
+
channel,
|
|
21701
|
+
messageId,
|
|
21702
|
+
summary,
|
|
21703
|
+
[[{ label: "\u2190 Back to Dashboard", data: "evolve:menu" }]]
|
|
21704
|
+
);
|
|
21171
21705
|
return;
|
|
21172
21706
|
}
|
|
21173
21707
|
const insightId = session2.insightIds[session2.currentIndex];
|
|
21174
21708
|
const insight = getInsightById2(getDb(), insightId);
|
|
21175
21709
|
if (!insight || insight.status !== "pending") {
|
|
21176
|
-
const { advanceReviewSession: advanceReviewSession2 } = await Promise.resolve().then(() => (init_store4(), store_exports4));
|
|
21177
21710
|
advanceReviewSession2(getDb(), chatId, insightId, "skipped");
|
|
21178
|
-
return sendCurrentProposal(chatId, channel);
|
|
21711
|
+
return sendCurrentProposal(chatId, channel, messageId);
|
|
21179
21712
|
}
|
|
21180
21713
|
const card = formatProposalCardWithProgress2(insight, session2.currentIndex, session2.insightIds.length);
|
|
21181
|
-
const kb =
|
|
21182
|
-
|
|
21714
|
+
const kb = [
|
|
21715
|
+
...buildProposalKeyboard2(insight.id, insight.category),
|
|
21716
|
+
[{ label: "\u2190 Dashboard", data: "evolve:menu" }]
|
|
21717
|
+
];
|
|
21718
|
+
await sendOrEditKeyboard(chatId, channel, messageId, card, kb);
|
|
21719
|
+
}
|
|
21720
|
+
function resolveBackendAccountLabel(chatId, backendId) {
|
|
21721
|
+
if (backendId === "gemini") {
|
|
21722
|
+
const slotId2 = getChatGeminiSlotId(chatId);
|
|
21723
|
+
if (slotId2 == null) return "Auto";
|
|
21724
|
+
const slots2 = getGeminiSlots();
|
|
21725
|
+
const slot2 = slots2.find((s) => s.id === slotId2);
|
|
21726
|
+
return slot2?.label ?? `slot-${slotId2}`;
|
|
21727
|
+
}
|
|
21728
|
+
const slotId = getChatBackendSlotId(chatId, backendId);
|
|
21729
|
+
if (slotId == null) return "Auto";
|
|
21730
|
+
const slots = getBackendSlots(backendId);
|
|
21731
|
+
const slot = slots.find((s) => s.id === slotId);
|
|
21732
|
+
return slot?.label ?? `slot-${slotId}`;
|
|
21733
|
+
}
|
|
21734
|
+
function backendPickerLabel(chatId, backendId, currentBackend) {
|
|
21735
|
+
const adapter = getAdapter(backendId);
|
|
21736
|
+
const model2 = backendId === currentBackend ? getModel(chatId) ?? adapter.defaultModel : adapter.defaultModel;
|
|
21737
|
+
const modelInfo = adapter.availableModels[model2];
|
|
21738
|
+
const modelShort = modelInfo?.label ?? model2;
|
|
21739
|
+
const thinking2 = backendId === currentBackend ? getThinkingLevel(chatId) ?? "auto" : "auto";
|
|
21740
|
+
const account = resolveBackendAccountLabel(chatId, backendId);
|
|
21741
|
+
const active = backendId === currentBackend ? "\u2713 " : "";
|
|
21742
|
+
return `${active}${adapter.displayName} \xB7 ${modelShort} \xB7 ${capitalize(thinking2)} \xB7 ${account}`;
|
|
21743
|
+
}
|
|
21744
|
+
function backendConfigSummary(chatId, backendId, switched) {
|
|
21745
|
+
const adapter = getAdapter(backendId);
|
|
21746
|
+
const model2 = getModel(chatId) ?? adapter.defaultModel;
|
|
21747
|
+
const modelInfo = adapter.availableModels[model2];
|
|
21748
|
+
const modelLabel = modelInfo?.label ?? model2;
|
|
21749
|
+
const thinking2 = getThinkingLevel(chatId) ?? "auto";
|
|
21750
|
+
const account = resolveBackendAccountLabel(chatId, backendId);
|
|
21751
|
+
const header2 = switched ? `\u2705 Switched to ${adapter.displayName}` : `\u2699\uFE0F ${adapter.displayName}`;
|
|
21752
|
+
return `${header2}
|
|
21753
|
+
|
|
21754
|
+
Model: ${modelLabel}
|
|
21755
|
+
Thinking: ${capitalize(thinking2)}
|
|
21756
|
+
Account: ${account}`;
|
|
21183
21757
|
}
|
|
21184
|
-
async function
|
|
21185
|
-
|
|
21186
|
-
const
|
|
21187
|
-
|
|
21188
|
-
|
|
21758
|
+
async function sendBackendPicker(chatId, channel, messageId) {
|
|
21759
|
+
if (typeof channel.sendKeyboard !== "function") return;
|
|
21760
|
+
const currentBackend = getBackend(chatId) ?? "";
|
|
21761
|
+
const ids = getAvailableChatBackendIds();
|
|
21762
|
+
const buttons = ids.map((id) => [{
|
|
21763
|
+
label: backendPickerLabel(chatId, id, currentBackend),
|
|
21764
|
+
data: `backend:${id}`,
|
|
21765
|
+
...id === currentBackend ? { style: "primary" } : {}
|
|
21766
|
+
}]);
|
|
21767
|
+
await sendOrEditKeyboard(chatId, channel, messageId, "Select AI backend:", buttons);
|
|
21768
|
+
}
|
|
21769
|
+
async function sendBackendConfigPanel(chatId, backendId, channel, messageId, switched = false) {
|
|
21770
|
+
if (typeof channel.sendKeyboard !== "function") return;
|
|
21771
|
+
const summary = backendConfigSummary(chatId, backendId, switched);
|
|
21772
|
+
const buttons = [
|
|
21773
|
+
[
|
|
21774
|
+
{ label: "Model", data: `bconf:model:${backendId}` },
|
|
21775
|
+
{ label: "Thinking", data: `bconf:thinking:${backendId}` },
|
|
21776
|
+
{ label: "Account", data: `bconf:account:${backendId}` }
|
|
21777
|
+
],
|
|
21778
|
+
[{ label: "\u2190 Back to Backends", data: "bconf:back" }]
|
|
21779
|
+
];
|
|
21780
|
+
await sendOrEditKeyboard(chatId, channel, messageId, summary, buttons);
|
|
21781
|
+
}
|
|
21782
|
+
async function sendBackendModelPicker(chatId, backendId, channel, messageId) {
|
|
21783
|
+
if (typeof channel.sendKeyboard !== "function") return;
|
|
21784
|
+
const adapter = getAdapter(backendId);
|
|
21785
|
+
const currentModel = getModel(chatId) ?? adapter.defaultModel;
|
|
21786
|
+
const summary = backendConfigSummary(chatId, backendId, false);
|
|
21787
|
+
const modelButtons = Object.entries(adapter.availableModels).map(([id, info]) => [{
|
|
21788
|
+
label: `${id === currentModel ? "\u2713 " : ""}${info.label}`,
|
|
21789
|
+
data: `bconf:setmodel:${backendId}:${id}`,
|
|
21790
|
+
...id === currentModel ? { style: "primary" } : {}
|
|
21791
|
+
}]);
|
|
21792
|
+
modelButtons.push([{ label: "\u2190 Back", data: `bconf:panel:${backendId}` }]);
|
|
21793
|
+
await sendOrEditKeyboard(chatId, channel, messageId, `${summary}
|
|
21794
|
+
|
|
21795
|
+
Pick a model:`, modelButtons);
|
|
21796
|
+
}
|
|
21797
|
+
async function sendBackendThinkingPicker(chatId, backendId, channel, messageId) {
|
|
21798
|
+
if (typeof channel.sendKeyboard !== "function") return;
|
|
21799
|
+
const adapter = getAdapter(backendId);
|
|
21800
|
+
const currentModel = getModel(chatId) ?? adapter.defaultModel;
|
|
21801
|
+
const modelInfo = adapter.availableModels[currentModel];
|
|
21802
|
+
const supportsThinking = modelInfo?.thinking === "adjustable";
|
|
21803
|
+
const summary = backendConfigSummary(chatId, backendId, false);
|
|
21804
|
+
if (!supportsThinking) {
|
|
21805
|
+
await sendOrEditKeyboard(
|
|
21806
|
+
chatId,
|
|
21807
|
+
channel,
|
|
21808
|
+
messageId,
|
|
21809
|
+
`${summary}
|
|
21810
|
+
|
|
21811
|
+
\u26A0\uFE0F Thinking is fixed for ${modelInfo?.label ?? currentModel}.`,
|
|
21812
|
+
[[{ label: "\u2190 Back", data: `bconf:panel:${backendId}` }]]
|
|
21813
|
+
);
|
|
21189
21814
|
return;
|
|
21190
21815
|
}
|
|
21191
|
-
|
|
21192
|
-
|
|
21816
|
+
const current = getThinkingLevel(chatId) ?? "auto";
|
|
21817
|
+
const levels = [
|
|
21818
|
+
["auto", "Auto"],
|
|
21819
|
+
["off", "Off"],
|
|
21820
|
+
["low", "Low"],
|
|
21821
|
+
["medium", "Medium"],
|
|
21822
|
+
["high", "High"],
|
|
21823
|
+
["extra_high", "Extra High"]
|
|
21824
|
+
];
|
|
21825
|
+
const buttons = levels.map(([val, label2]) => [{
|
|
21826
|
+
label: `${val === current ? "\u2713 " : ""}${label2}`,
|
|
21827
|
+
data: `bconf:setthinking:${backendId}:${val}`,
|
|
21828
|
+
...val === current ? { style: "primary" } : {}
|
|
21829
|
+
}]);
|
|
21830
|
+
buttons.push([{ label: "\u2190 Back", data: `bconf:panel:${backendId}` }]);
|
|
21831
|
+
await sendOrEditKeyboard(chatId, channel, messageId, `${summary}
|
|
21832
|
+
|
|
21833
|
+
Pick thinking level:`, buttons);
|
|
21834
|
+
}
|
|
21835
|
+
async function sendBackendAccountPicker(chatId, backendId, channel, messageId) {
|
|
21836
|
+
if (typeof channel.sendKeyboard !== "function") return;
|
|
21837
|
+
const summary = backendConfigSummary(chatId, backendId, false);
|
|
21838
|
+
const isGemini = backendId === "gemini";
|
|
21839
|
+
const slots = isGemini ? getGeminiSlots() : getBackendSlots(backendId);
|
|
21840
|
+
const enabledSlots = slots.filter((s) => s.enabled !== 0 && s.enabled !== false);
|
|
21841
|
+
if (enabledSlots.length === 0) {
|
|
21842
|
+
await sendOrEditKeyboard(
|
|
21193
21843
|
chatId,
|
|
21194
|
-
|
|
21195
|
-
|
|
21196
|
-
|
|
21197
|
-
|
|
21198
|
-
|
|
21199
|
-
|
|
21200
|
-
],
|
|
21201
|
-
[{ label: "Cancel", data: `backend_cancel:${target}` }]
|
|
21202
|
-
]
|
|
21844
|
+
channel,
|
|
21845
|
+
messageId,
|
|
21846
|
+
`${summary}
|
|
21847
|
+
|
|
21848
|
+
\u26A0\uFE0F No accounts configured. Add one via /accounts.`,
|
|
21849
|
+
[[{ label: "\u2190 Back", data: `bconf:panel:${backendId}` }]]
|
|
21203
21850
|
);
|
|
21204
|
-
|
|
21205
|
-
await doBackendSwitch(chatId, target, channel);
|
|
21851
|
+
return;
|
|
21206
21852
|
}
|
|
21853
|
+
const activeSlotId = isGemini ? getChatGeminiSlotId(chatId) : getChatBackendSlotId(chatId, backendId);
|
|
21854
|
+
const buttons = enabledSlots.map((s) => {
|
|
21855
|
+
const label2 = s.label ?? `slot-${s.id}`;
|
|
21856
|
+
const icon = (s.slotType ?? s.slot_type) === "oauth" ? "\u{1F464}" : "\u{1F511}";
|
|
21857
|
+
const active = activeSlotId === s.id;
|
|
21858
|
+
return [{
|
|
21859
|
+
label: `${active ? "\u2713 " : ""}${icon} ${label2}`,
|
|
21860
|
+
data: `bconf:setaccount:${backendId}:${s.id}`,
|
|
21861
|
+
...active ? { style: "primary" } : {}
|
|
21862
|
+
}];
|
|
21863
|
+
});
|
|
21864
|
+
buttons.push([{ label: "\u2190 Auto (rotate)", data: `bconf:setaccount:${backendId}:auto` }]);
|
|
21865
|
+
buttons.push([{ label: "\u2190 Back", data: `bconf:panel:${backendId}` }]);
|
|
21866
|
+
await sendOrEditKeyboard(chatId, channel, messageId, `${summary}
|
|
21867
|
+
|
|
21868
|
+
Pick an account:`, buttons);
|
|
21207
21869
|
}
|
|
21208
|
-
async function
|
|
21209
|
-
const
|
|
21210
|
-
const
|
|
21211
|
-
if (
|
|
21212
|
-
await
|
|
21213
|
-
|
|
21214
|
-
const summarized = await summarizeWithFallbackChain(chatId, backendId);
|
|
21215
|
-
const bridge = buildContextBridge(chatId);
|
|
21216
|
-
if (bridge) {
|
|
21217
|
-
setPendingContextBridge(chatId, bridge);
|
|
21870
|
+
async function sendBackendSwitchConfirmation(chatId, target, channel, messageId) {
|
|
21871
|
+
const current = getBackend(chatId);
|
|
21872
|
+
const targetAdapter = getAdapter(target);
|
|
21873
|
+
if (current === target) {
|
|
21874
|
+
await sendBackendConfigPanel(chatId, target, channel, messageId, false);
|
|
21875
|
+
return;
|
|
21218
21876
|
}
|
|
21219
|
-
|
|
21220
|
-
|
|
21221
|
-
|
|
21222
|
-
|
|
21877
|
+
await doBackendSwitch(chatId, target, channel, { messageId });
|
|
21878
|
+
}
|
|
21879
|
+
async function doBackendSwitch(chatId, backendId, channel, opts) {
|
|
21880
|
+
const targetAdapter = getAdapter(backendId);
|
|
21881
|
+
if (!opts?.skipContext) {
|
|
21882
|
+
const pairCount = getMessagePairCount(chatId);
|
|
21883
|
+
if (pairCount >= 2) {
|
|
21884
|
+
if (opts?.messageId && typeof channel.editKeyboard === "function") {
|
|
21885
|
+
await channel.editKeyboard(chatId, opts.messageId, `\u23F3 Saving context before switching to ${targetAdapter.displayName}...`, []);
|
|
21886
|
+
} else {
|
|
21887
|
+
await channel.sendText(chatId, `\u23F3 Saving context...`, { parseMode: "plain" });
|
|
21888
|
+
}
|
|
21889
|
+
}
|
|
21890
|
+
const summarized = await summarizeWithFallbackChain(chatId, backendId);
|
|
21891
|
+
const bridge = buildContextBridge(chatId);
|
|
21892
|
+
if (bridge) setPendingContextBridge(chatId, bridge);
|
|
21223
21893
|
}
|
|
21224
21894
|
clearSession(chatId);
|
|
21225
21895
|
clearModel(chatId);
|
|
21226
21896
|
clearThinkingLevel(chatId);
|
|
21227
|
-
if (backendId !== "gemini")
|
|
21228
|
-
clearChatGeminiSlot(chatId);
|
|
21229
|
-
}
|
|
21897
|
+
if (backendId !== "gemini") clearChatGeminiSlot(chatId);
|
|
21230
21898
|
setBackend(chatId, backendId);
|
|
21231
|
-
logActivity(getDb(), {
|
|
21232
|
-
|
|
21233
|
-
|
|
21234
|
-
|
|
21235
|
-
|
|
21236
|
-
|
|
21237
|
-
|
|
21238
|
-
|
|
21239
|
-
rows.push([{ label: "\u2705 Use Default", data: `backend_dismiss:${backendId}` }]);
|
|
21240
|
-
await channel.sendKeyboard(
|
|
21241
|
-
chatId,
|
|
21242
|
-
`\u2705 Switched to ${targetAdapter.displayName}.
|
|
21243
|
-
|
|
21244
|
-
Choose a model (optional):`,
|
|
21245
|
-
rows
|
|
21246
|
-
);
|
|
21247
|
-
} else {
|
|
21248
|
-
await channel.sendText(chatId, `\u2705 Switched to ${targetAdapter.displayName}. Ready!`, { parseMode: "plain" });
|
|
21249
|
-
}
|
|
21899
|
+
logActivity(getDb(), {
|
|
21900
|
+
chatId,
|
|
21901
|
+
source: "telegram",
|
|
21902
|
+
eventType: "config_changed",
|
|
21903
|
+
summary: `Backend switched to ${targetAdapter.displayName}`,
|
|
21904
|
+
detail: { field: "backend", value: backendId }
|
|
21905
|
+
});
|
|
21906
|
+
await sendBackendConfigPanel(chatId, backendId, channel, opts?.messageId, true);
|
|
21250
21907
|
}
|
|
21251
21908
|
var ROTATION_MODE_LABELS, ROTATION_MODE_CONFIRM_LABELS;
|
|
21252
21909
|
var init_ui = __esm({
|
|
@@ -21255,6 +21912,7 @@ var init_ui = __esm({
|
|
|
21255
21912
|
init_format();
|
|
21256
21913
|
init_backends();
|
|
21257
21914
|
init_store5();
|
|
21915
|
+
init_chat_settings();
|
|
21258
21916
|
init_summarize();
|
|
21259
21917
|
init_inject();
|
|
21260
21918
|
init_session_log();
|
|
@@ -21299,237 +21957,285 @@ var init_resolve = __esm({
|
|
|
21299
21957
|
|
|
21300
21958
|
// src/router/evolve.ts
|
|
21301
21959
|
import { homedir as homedir6 } from "os";
|
|
21302
|
-
|
|
21303
|
-
const
|
|
21960
|
+
function relativeTime(isoUtc) {
|
|
21961
|
+
const diffMs = Date.now() - (/* @__PURE__ */ new Date(isoUtc + "Z")).getTime();
|
|
21962
|
+
const h = Math.floor(diffMs / 36e5);
|
|
21963
|
+
if (h < 1) return "< 1 hour ago";
|
|
21964
|
+
if (h < 24) return `${h}h ago`;
|
|
21965
|
+
return `${Math.floor(h / 24)}d ago`;
|
|
21966
|
+
}
|
|
21967
|
+
async function getReflectionChatId(chatId) {
|
|
21304
21968
|
const { getPrimaryChatId: getPrimaryChatId2 } = await Promise.resolve().then(() => (init_resolve(), resolve_exports));
|
|
21305
|
-
|
|
21306
|
-
const isActive = getRefStatus(getDb(), reflectionChatId) === "active";
|
|
21307
|
-
if (!isActive) {
|
|
21308
|
-
const text = [
|
|
21309
|
-
"Self-Learning & Evolution",
|
|
21310
|
-
"\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501",
|
|
21311
|
-
"",
|
|
21312
|
-
"Teach your assistant to improve over time.",
|
|
21313
|
-
"",
|
|
21314
|
-
"When enabled, CC-Claw watches for corrections,",
|
|
21315
|
-
"preferences, and frustration in your messages,",
|
|
21316
|
-
"then proposes changes to its personality and",
|
|
21317
|
-
"behavior files (SOUL.md, USER.md).",
|
|
21318
|
-
"",
|
|
21319
|
-
"You review and approve every change."
|
|
21320
|
-
].join("\n");
|
|
21321
|
-
if (typeof channel.sendKeyboard === "function") {
|
|
21322
|
-
const { buildEvolveOnboardingKeyboard: buildEvolveOnboardingKeyboard2 } = await Promise.resolve().then(() => (init_propose(), propose_exports));
|
|
21323
|
-
await channel.sendKeyboard(chatId, text, buildEvolveOnboardingKeyboard2());
|
|
21324
|
-
} else {
|
|
21325
|
-
await channel.sendText(chatId, text + "\n\nUse /evolve enable to activate.", { parseMode: "plain" });
|
|
21326
|
-
}
|
|
21327
|
-
} else {
|
|
21328
|
-
await sendEvolveDashboard(chatId, reflectionChatId, channel);
|
|
21329
|
-
}
|
|
21969
|
+
return getPrimaryChatId2() || chatId;
|
|
21330
21970
|
}
|
|
21331
|
-
async function
|
|
21971
|
+
async function buildDashboardView(chatId, reflChatId) {
|
|
21332
21972
|
const { getUnprocessedSignalCount: getUnprocessedSignalCount2, getPendingInsightCount: getPendingInsightCount2, getLastAnalysisTime: getLastAnalysisTime2, getReflectionModelConfig: getReflectionModelConfig2 } = await Promise.resolve().then(() => (init_store4(), store_exports4));
|
|
21333
|
-
const signals = getUnprocessedSignalCount2(getDb(),
|
|
21334
|
-
const pending = getPendingInsightCount2(getDb(),
|
|
21335
|
-
const lastTime = getLastAnalysisTime2(getDb(),
|
|
21336
|
-
|
|
21337
|
-
|
|
21338
|
-
|
|
21339
|
-
|
|
21340
|
-
if (diffHours < 1) lastText = "< 1 hour ago";
|
|
21341
|
-
else if (diffHours < 24) lastText = `${diffHours}h ago`;
|
|
21342
|
-
else lastText = `${Math.floor(diffHours / 24)}d ago`;
|
|
21343
|
-
}
|
|
21344
|
-
const modelConfig = getReflectionModelConfig2(getDb(), reflectionChatId);
|
|
21345
|
-
const modelLabel = modelConfig.mode === "pinned" && modelConfig.backend && modelConfig.model ? `Pinned: ${modelConfig.backend}/${modelConfig.model}` : modelConfig.mode === "cheap" ? "Cheap" : "Auto";
|
|
21346
|
-
const dashText = [
|
|
21973
|
+
const signals = getUnprocessedSignalCount2(getDb(), reflChatId);
|
|
21974
|
+
const pending = getPendingInsightCount2(getDb(), reflChatId);
|
|
21975
|
+
const lastTime = getLastAnalysisTime2(getDb(), reflChatId);
|
|
21976
|
+
const lastText = lastTime ? relativeTime(lastTime) : "never";
|
|
21977
|
+
const modelConfig = getReflectionModelConfig2(getDb(), reflChatId);
|
|
21978
|
+
const modelLabel = modelConfig.mode === "pinned" && modelConfig.backend && modelConfig.model ? `${modelConfig.backend}/${modelConfig.model}` : modelConfig.mode === "cheap" ? "Cheap" : "Auto";
|
|
21979
|
+
const text = [
|
|
21347
21980
|
"Self-Learning & Evolution",
|
|
21348
21981
|
"\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501",
|
|
21349
21982
|
"",
|
|
21350
|
-
|
|
21983
|
+
"\u2705 Active",
|
|
21351
21984
|
`Signals: ${signals} pending \xB7 Proposals: ${pending}`,
|
|
21352
21985
|
`Last analysis: ${lastText}`,
|
|
21353
21986
|
`Model: ${modelLabel}`
|
|
21354
21987
|
].join("\n");
|
|
21355
|
-
|
|
21356
|
-
|
|
21357
|
-
|
|
21988
|
+
const reviewLabel = pending > 0 ? `Review (${pending})` : "Review";
|
|
21989
|
+
const buttons = [
|
|
21990
|
+
[
|
|
21991
|
+
{ label: "\u{1F50D} Analyze Now", data: "evolve:analyze", style: "success" },
|
|
21992
|
+
{ label: reviewLabel, data: "evolve:review", ...pending > 0 ? { style: "primary" } : {} }
|
|
21993
|
+
],
|
|
21994
|
+
[
|
|
21995
|
+
{ label: "Stats", data: "evolve:stats" },
|
|
21996
|
+
{ label: "History", data: "evolve:history" }
|
|
21997
|
+
],
|
|
21998
|
+
[
|
|
21999
|
+
{ label: "Model", data: "evolve:model" },
|
|
22000
|
+
{ label: "Undo", data: "evolve:undo" },
|
|
22001
|
+
{ label: "\u23F8 Disable", data: "evolve:toggle", style: "danger" }
|
|
22002
|
+
]
|
|
22003
|
+
];
|
|
22004
|
+
return { text, buttons };
|
|
22005
|
+
}
|
|
22006
|
+
async function sendEvolveDashboard(chatId, reflChatId, channel, messageId) {
|
|
22007
|
+
const { text, buttons } = await buildDashboardView(chatId, reflChatId);
|
|
22008
|
+
await sendOrEditKeyboard(chatId, channel, messageId, text, buttons);
|
|
22009
|
+
}
|
|
22010
|
+
function buildOnboardingView() {
|
|
22011
|
+
const text = [
|
|
22012
|
+
"Self-Learning & Evolution",
|
|
22013
|
+
"\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501",
|
|
22014
|
+
"",
|
|
22015
|
+
"Teach your assistant to improve over time.",
|
|
22016
|
+
"",
|
|
22017
|
+
"CC-Claw watches for corrections, preferences,",
|
|
22018
|
+
"and frustration in your messages, then proposes",
|
|
22019
|
+
"changes to its personality files (SOUL.md, USER.md).",
|
|
22020
|
+
"",
|
|
22021
|
+
"You review and approve every change."
|
|
22022
|
+
].join("\n");
|
|
22023
|
+
const buttons = [
|
|
22024
|
+
[{ label: "\u2705 Enable Self-Learning", data: "evolve:toggle", style: "success" }],
|
|
22025
|
+
[{ label: "One-Time Analysis", data: "evolve:analyze" }]
|
|
22026
|
+
];
|
|
22027
|
+
return { text, buttons };
|
|
22028
|
+
}
|
|
22029
|
+
async function sendCurrentProposalInPlace(chatId, channel, messageId) {
|
|
22030
|
+
const { getReviewSession: getReviewSession2, getInsightById: getInsightById2, deleteReviewSession: deleteReviewSession2, advanceReviewSession: advanceReviewSession2 } = await Promise.resolve().then(() => (init_store4(), store_exports4));
|
|
22031
|
+
const { formatProposalCardWithProgress: formatProposalCardWithProgress2, buildProposalKeyboard: buildProposalKeyboard2, buildReviewCompleteMessage: buildReviewCompleteMessage3 } = await Promise.resolve().then(() => (init_propose(), propose_exports));
|
|
22032
|
+
const reflChatId = await getReflectionChatId(chatId);
|
|
22033
|
+
const session2 = getReviewSession2(getDb(), chatId);
|
|
22034
|
+
if (!session2) {
|
|
22035
|
+
await sendEvolveDashboard(chatId, reflChatId, channel, messageId);
|
|
22036
|
+
return;
|
|
22037
|
+
}
|
|
22038
|
+
if (session2.currentIndex >= session2.insightIds.length) {
|
|
22039
|
+
const summary = buildReviewCompleteMessage3(session2.results);
|
|
22040
|
+
deleteReviewSession2(getDb(), chatId);
|
|
22041
|
+
const buttons = [[{ label: "\u2190 Back to Dashboard", data: "evolve:menu" }]];
|
|
22042
|
+
await sendOrEditKeyboard(chatId, channel, messageId, summary, buttons);
|
|
22043
|
+
return;
|
|
22044
|
+
}
|
|
22045
|
+
const insightId = session2.insightIds[session2.currentIndex];
|
|
22046
|
+
const insight = getInsightById2(getDb(), insightId);
|
|
22047
|
+
if (!insight || insight.status !== "pending") {
|
|
22048
|
+
advanceReviewSession2(getDb(), chatId, insightId, "skipped");
|
|
22049
|
+
return sendCurrentProposalInPlace(chatId, channel, messageId);
|
|
22050
|
+
}
|
|
22051
|
+
const card = formatProposalCardWithProgress2(insight, session2.currentIndex, session2.insightIds.length);
|
|
22052
|
+
const kb = [
|
|
22053
|
+
...buildProposalKeyboard2(insight.id, insight.category),
|
|
22054
|
+
[{ label: "\u2190 Dashboard", data: "evolve:menu" }]
|
|
22055
|
+
];
|
|
22056
|
+
await sendOrEditKeyboard(chatId, channel, messageId, card, kb);
|
|
22057
|
+
}
|
|
22058
|
+
async function handleEvolveCommand(chatId, channel) {
|
|
22059
|
+
const { getReflectionStatus: getReflectionStatus2 } = await Promise.resolve().then(() => (init_store4(), store_exports4));
|
|
22060
|
+
const reflChatId = await getReflectionChatId(chatId);
|
|
22061
|
+
const isActive = getReflectionStatus2(getDb(), reflChatId) === "active";
|
|
22062
|
+
if (typeof channel.sendKeyboard !== "function") {
|
|
22063
|
+
await channel.sendText(chatId, isActive ? "Use /evolve buttons to manage self-learning." : "Self-learning disabled. Use /evolve enable.", { parseMode: "plain" });
|
|
22064
|
+
return;
|
|
22065
|
+
}
|
|
22066
|
+
if (!isActive) {
|
|
22067
|
+
const { text, buttons } = buildOnboardingView();
|
|
22068
|
+
await channel.sendKeyboard(chatId, text, buttons);
|
|
21358
22069
|
} else {
|
|
21359
|
-
await
|
|
22070
|
+
await sendEvolveDashboard(chatId, reflChatId, channel);
|
|
21360
22071
|
}
|
|
21361
22072
|
}
|
|
21362
|
-
async function handleEvolveCallback(chatId, data, channel) {
|
|
22073
|
+
async function handleEvolveCallback(chatId, data, channel, messageId) {
|
|
21363
22074
|
const parts = data.split(":");
|
|
21364
22075
|
const action = parts[1];
|
|
21365
22076
|
const idStr = parts[2];
|
|
21366
22077
|
if (action !== "discuss") {
|
|
21367
|
-
const { clearDiscussionMode:
|
|
21368
|
-
|
|
22078
|
+
const { clearDiscussionMode: clearDiscussionMode2 } = await Promise.resolve().then(() => (init_store4(), store_exports4));
|
|
22079
|
+
clearDiscussionMode2(chatId);
|
|
21369
22080
|
}
|
|
22081
|
+
const reflChatId = await getReflectionChatId(chatId);
|
|
21370
22082
|
switch (action) {
|
|
21371
22083
|
case "menu": {
|
|
21372
|
-
const { getReflectionStatus:
|
|
21373
|
-
const
|
|
21374
|
-
|
|
21375
|
-
|
|
21376
|
-
|
|
21377
|
-
const { buildEvolveOnboardingKeyboard: buildEvolveOnboardingKeyboard2 } = await Promise.resolve().then(() => (init_propose(), propose_exports));
|
|
21378
|
-
const text = [
|
|
21379
|
-
"Self-Learning & Evolution",
|
|
21380
|
-
"\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501",
|
|
21381
|
-
"",
|
|
21382
|
-
"Teach your assistant to improve over time.",
|
|
21383
|
-
"",
|
|
21384
|
-
"When enabled, CC-Claw watches for corrections,",
|
|
21385
|
-
"preferences, and frustration in your messages,",
|
|
21386
|
-
"then proposes changes to its personality and",
|
|
21387
|
-
"behavior files (SOUL.md, USER.md).",
|
|
21388
|
-
"",
|
|
21389
|
-
"You review and approve every change."
|
|
21390
|
-
].join("\n");
|
|
21391
|
-
await channel.sendKeyboard(chatId, text, buildEvolveOnboardingKeyboard2());
|
|
22084
|
+
const { getReflectionStatus: getReflectionStatus2 } = await Promise.resolve().then(() => (init_store4(), store_exports4));
|
|
22085
|
+
const isActive = getReflectionStatus2(getDb(), reflChatId) === "active";
|
|
22086
|
+
if (!isActive) {
|
|
22087
|
+
const { text, buttons } = buildOnboardingView();
|
|
22088
|
+
await sendOrEditKeyboard(chatId, channel, messageId, text, buttons);
|
|
21392
22089
|
} else {
|
|
21393
|
-
await sendEvolveDashboard(chatId, reflChatId, channel);
|
|
22090
|
+
await sendEvolveDashboard(chatId, reflChatId, channel, messageId);
|
|
21394
22091
|
}
|
|
21395
22092
|
break;
|
|
21396
22093
|
}
|
|
21397
22094
|
case "analyze": {
|
|
21398
22095
|
const { runAnalysis: runAnalysis2 } = await Promise.resolve().then(() => (init_analyze(), analyze_exports));
|
|
21399
|
-
|
|
21400
|
-
|
|
22096
|
+
await sendOrEditKeyboard(
|
|
22097
|
+
chatId,
|
|
22098
|
+
channel,
|
|
22099
|
+
messageId,
|
|
22100
|
+
"\u{1F50D} Analyzing recent interactions...\n\nThis may take a moment.",
|
|
22101
|
+
[]
|
|
22102
|
+
);
|
|
21401
22103
|
try {
|
|
21402
22104
|
const insights = await runAnalysis2(chatId, { force: true });
|
|
21403
22105
|
if (insights.length === 0) {
|
|
21404
|
-
await
|
|
22106
|
+
await sendOrEditKeyboard(
|
|
22107
|
+
chatId,
|
|
22108
|
+
channel,
|
|
22109
|
+
messageId,
|
|
22110
|
+
"\u2705 Analysis complete.\n\nNo new insights from recent interactions.",
|
|
22111
|
+
[[{ label: "\u2190 Back to Dashboard", data: "evolve:menu" }]]
|
|
22112
|
+
);
|
|
21405
22113
|
} else {
|
|
21406
|
-
|
|
21407
|
-
|
|
22114
|
+
await sendOrEditKeyboard(
|
|
22115
|
+
chatId,
|
|
22116
|
+
channel,
|
|
22117
|
+
messageId,
|
|
22118
|
+
`\u2705 Analysis complete \u2014 ${insights.length} new proposal(s) ready.`,
|
|
22119
|
+
[
|
|
22120
|
+
[{ label: `Review (${insights.length})`, data: "evolve:review", style: "primary" }],
|
|
22121
|
+
[{ label: "\u2190 Back to Dashboard", data: "evolve:menu" }]
|
|
22122
|
+
]
|
|
22123
|
+
);
|
|
21408
22124
|
}
|
|
21409
22125
|
} catch (e) {
|
|
21410
|
-
await
|
|
22126
|
+
await sendOrEditKeyboard(
|
|
22127
|
+
chatId,
|
|
22128
|
+
channel,
|
|
22129
|
+
messageId,
|
|
22130
|
+
`\u274C Analysis failed: ${e}`,
|
|
22131
|
+
[[{ label: "\u2190 Back to Dashboard", data: "evolve:menu" }]]
|
|
22132
|
+
);
|
|
21411
22133
|
}
|
|
21412
22134
|
break;
|
|
21413
22135
|
}
|
|
21414
22136
|
case "review": {
|
|
21415
22137
|
const { getPendingInsights: getPendingInsights2, createReviewSession: createReviewSession2 } = await Promise.resolve().then(() => (init_store4(), store_exports4));
|
|
21416
|
-
const { getPrimaryChatId: getPrimary } = await Promise.resolve().then(() => (init_resolve(), resolve_exports));
|
|
21417
|
-
const reflChatId = getPrimary() || chatId;
|
|
21418
22138
|
const pending = getPendingInsights2(getDb(), reflChatId);
|
|
21419
22139
|
if (pending.length === 0) {
|
|
21420
|
-
await
|
|
21421
|
-
|
|
21422
|
-
|
|
21423
|
-
|
|
21424
|
-
|
|
21425
|
-
|
|
21426
|
-
|
|
21427
|
-
break;
|
|
21428
|
-
}
|
|
21429
|
-
case "preview": {
|
|
21430
|
-
const { getInsightById: pvIns } = await Promise.resolve().then(() => (init_store4(), store_exports4));
|
|
21431
|
-
const pvInsight = pvIns(getDb(), parseInt(idStr, 10));
|
|
21432
|
-
if (!pvInsight) {
|
|
21433
|
-
await channel.sendText(chatId, "Proposal not found.", { parseMode: "plain" });
|
|
21434
|
-
break;
|
|
21435
|
-
}
|
|
21436
|
-
if (!pvInsight.targetFile || !pvInsight.proposedDiff) {
|
|
21437
|
-
await channel.sendText(chatId, "No diff available for this proposal.", { parseMode: "plain" });
|
|
21438
|
-
break;
|
|
21439
|
-
}
|
|
21440
|
-
const { readFileSync: pvRead, existsSync: pvExists } = await import("fs");
|
|
21441
|
-
const { join: pvJoin } = await import("path");
|
|
21442
|
-
const pvTargetPath = pvJoin(homedir6(), ".cc-claw", pvInsight.targetFile);
|
|
21443
|
-
let previewLines;
|
|
21444
|
-
if (pvExists(pvTargetPath)) {
|
|
21445
|
-
const currentContent = pvRead(pvTargetPath, "utf-8");
|
|
21446
|
-
const diffLines = pvInsight.proposedDiff.split("\n");
|
|
21447
|
-
const additions = diffLines.filter((l) => l.startsWith("+")).map((l) => l.slice(1).trim());
|
|
21448
|
-
const removals = diffLines.filter((l) => l.startsWith("-")).map((l) => l.slice(1).trim());
|
|
21449
|
-
previewLines = [
|
|
21450
|
-
`\u{1F4CB} Dry-run preview: ${pvInsight.targetFile}`,
|
|
21451
|
-
`${"\u2500".repeat(40)}`,
|
|
21452
|
-
""
|
|
21453
|
-
];
|
|
21454
|
-
if (removals.length > 0) {
|
|
21455
|
-
previewLines.push("Lines to REMOVE:");
|
|
21456
|
-
for (const r of removals) {
|
|
21457
|
-
previewLines.push(` - ${r}`);
|
|
21458
|
-
const idx = currentContent.indexOf(r);
|
|
21459
|
-
if (idx >= 0) {
|
|
21460
|
-
const lineNum = currentContent.slice(0, idx).split("\n").length;
|
|
21461
|
-
previewLines.push(` (line ~${lineNum})`);
|
|
21462
|
-
}
|
|
21463
|
-
}
|
|
21464
|
-
previewLines.push("");
|
|
21465
|
-
}
|
|
21466
|
-
if (additions.length > 0) {
|
|
21467
|
-
previewLines.push("Lines to ADD:");
|
|
21468
|
-
for (const a of additions) {
|
|
21469
|
-
previewLines.push(` + ${a}`);
|
|
21470
|
-
}
|
|
21471
|
-
previewLines.push("");
|
|
21472
|
-
}
|
|
21473
|
-
previewLines.push(`File size: ${currentContent.length} chars`);
|
|
22140
|
+
await sendOrEditKeyboard(
|
|
22141
|
+
chatId,
|
|
22142
|
+
channel,
|
|
22143
|
+
messageId,
|
|
22144
|
+
"No pending proposals.",
|
|
22145
|
+
[[{ label: "\u2190 Back to Dashboard", data: "evolve:menu" }]]
|
|
22146
|
+
);
|
|
21474
22147
|
} else {
|
|
21475
|
-
|
|
21476
|
-
|
|
21477
|
-
`${"\u2500".repeat(40)}`,
|
|
21478
|
-
"",
|
|
21479
|
-
"(File does not exist yet \u2014 will be created)",
|
|
21480
|
-
"",
|
|
21481
|
-
"Content to add:",
|
|
21482
|
-
pvInsight.proposedDiff
|
|
21483
|
-
];
|
|
21484
|
-
}
|
|
21485
|
-
await channel.sendText(chatId, previewLines.join("\n"), { parseMode: "plain" });
|
|
21486
|
-
break;
|
|
21487
|
-
}
|
|
21488
|
-
case "diff": {
|
|
21489
|
-
const { getInsightById: getInsightById2 } = await Promise.resolve().then(() => (init_store4(), store_exports4));
|
|
21490
|
-
const { formatDiffCodeBlock: formatDiffCodeBlock2 } = await Promise.resolve().then(() => (init_propose(), propose_exports));
|
|
21491
|
-
const insight = getInsightById2(getDb(), parseInt(idStr, 10));
|
|
21492
|
-
if (insight?.proposedDiff) {
|
|
21493
|
-
await channel.sendText(chatId, formatDiffCodeBlock2(insight.proposedDiff), { parseMode: "plain" });
|
|
22148
|
+
createReviewSession2(getDb(), chatId, pending.map((p) => p.id));
|
|
22149
|
+
await sendCurrentProposalInPlace(chatId, channel, messageId);
|
|
21494
22150
|
}
|
|
21495
22151
|
break;
|
|
21496
22152
|
}
|
|
21497
22153
|
case "apply": {
|
|
21498
22154
|
const { applyInsight: applyInsight2 } = await Promise.resolve().then(() => (init_apply(), apply_exports));
|
|
22155
|
+
const { advanceReviewSession: advanceReviewSession2 } = await Promise.resolve().then(() => (init_store4(), store_exports4));
|
|
21499
22156
|
const result = await applyInsight2(parseInt(idStr, 10));
|
|
21500
|
-
|
|
21501
|
-
|
|
21502
|
-
|
|
21503
|
-
|
|
22157
|
+
advanceReviewSession2(getDb(), chatId, parseInt(idStr, 10), "applied");
|
|
22158
|
+
await sendOrEditKeyboard(
|
|
22159
|
+
chatId,
|
|
22160
|
+
channel,
|
|
22161
|
+
messageId,
|
|
22162
|
+
`\u2705 ${result.message}`,
|
|
22163
|
+
[]
|
|
22164
|
+
);
|
|
22165
|
+
await new Promise((r) => setTimeout(r, 800));
|
|
22166
|
+
await sendCurrentProposalInPlace(chatId, channel, messageId);
|
|
21504
22167
|
break;
|
|
21505
22168
|
}
|
|
21506
22169
|
case "skip": {
|
|
21507
|
-
|
|
21508
|
-
|
|
21509
|
-
|
|
21510
|
-
|
|
22170
|
+
const { advanceReviewSession: advanceReviewSession2 } = await Promise.resolve().then(() => (init_store4(), store_exports4));
|
|
22171
|
+
advanceReviewSession2(getDb(), chatId, parseInt(idStr, 10), "skipped");
|
|
22172
|
+
await sendCurrentProposalInPlace(chatId, channel, messageId);
|
|
22173
|
+
break;
|
|
22174
|
+
}
|
|
22175
|
+
case "reject": {
|
|
22176
|
+
const { updateInsightStatus: updateInsightStatus2, advanceReviewSession: advanceReviewSession2 } = await Promise.resolve().then(() => (init_store4(), store_exports4));
|
|
22177
|
+
updateInsightStatus2(getDb(), parseInt(idStr, 10), "rejected");
|
|
22178
|
+
advanceReviewSession2(getDb(), chatId, parseInt(idStr, 10), "rejected");
|
|
22179
|
+
await sendCurrentProposalInPlace(chatId, channel, messageId);
|
|
21511
22180
|
break;
|
|
21512
22181
|
}
|
|
21513
22182
|
case "discuss": {
|
|
21514
|
-
const insId = parseInt(idStr, 10);
|
|
21515
22183
|
const { setDiscussionMode: setDiscussionMode2 } = await Promise.resolve().then(() => (init_store4(), store_exports4));
|
|
21516
|
-
setDiscussionMode2(chatId,
|
|
21517
|
-
await
|
|
21518
|
-
|
|
21519
|
-
|
|
21520
|
-
|
|
21521
|
-
|
|
21522
|
-
|
|
21523
|
-
|
|
22184
|
+
setDiscussionMode2(chatId, parseInt(idStr, 10));
|
|
22185
|
+
await sendOrEditKeyboard(
|
|
22186
|
+
chatId,
|
|
22187
|
+
channel,
|
|
22188
|
+
messageId,
|
|
22189
|
+
[
|
|
22190
|
+
"\u{1F4AC} Discussion mode",
|
|
22191
|
+
"",
|
|
22192
|
+
"Tell me what you'd like to change about this proposal.",
|
|
22193
|
+
"",
|
|
22194
|
+
'Examples: "Move this to a skill instead", "Make it shorter", "Why this file?"',
|
|
22195
|
+
"",
|
|
22196
|
+
"Type your reply below \u2014 I'll revise and come back with a new proposal."
|
|
22197
|
+
].join("\n"),
|
|
22198
|
+
[[{ label: "\u2190 Cancel discussion", data: `evolve:review` }]]
|
|
22199
|
+
);
|
|
21524
22200
|
break;
|
|
21525
22201
|
}
|
|
21526
|
-
case "
|
|
21527
|
-
const {
|
|
21528
|
-
|
|
21529
|
-
|
|
21530
|
-
|
|
21531
|
-
|
|
21532
|
-
|
|
22202
|
+
case "preview": {
|
|
22203
|
+
const { getInsightById: getInsightById2 } = await Promise.resolve().then(() => (init_store4(), store_exports4));
|
|
22204
|
+
const insight = getInsightById2(getDb(), parseInt(idStr, 10));
|
|
22205
|
+
if (!insight?.targetFile || !insight?.proposedDiff) {
|
|
22206
|
+
await sendOrEditKeyboard(
|
|
22207
|
+
chatId,
|
|
22208
|
+
channel,
|
|
22209
|
+
messageId,
|
|
22210
|
+
"No diff available for this proposal.",
|
|
22211
|
+
[[{ label: "\u2190 Back", data: `evolve:review` }]]
|
|
22212
|
+
);
|
|
22213
|
+
break;
|
|
22214
|
+
}
|
|
22215
|
+
const { readFileSync: readFileSync33, existsSync: existsSync62 } = await import("fs");
|
|
22216
|
+
const { join: join41 } = await import("path");
|
|
22217
|
+
const targetPath = join41(homedir6(), ".cc-claw", insight.targetFile);
|
|
22218
|
+
let previewText;
|
|
22219
|
+
if (existsSync62(targetPath)) {
|
|
22220
|
+
const current = readFileSync33(targetPath, "utf-8");
|
|
22221
|
+
const diffLines = insight.proposedDiff.split("\n");
|
|
22222
|
+
const additions = diffLines.filter((l) => l.startsWith("+")).map((l) => ` + ${l.slice(1).trim()}`);
|
|
22223
|
+
const removals = diffLines.filter((l) => l.startsWith("-")).map((l) => ` - ${l.slice(1).trim()}`);
|
|
22224
|
+
const lines = [`\u{1F4CB} Preview: ${insight.targetFile}`, ""];
|
|
22225
|
+
if (removals.length) lines.push("Lines to REMOVE:", ...removals, "");
|
|
22226
|
+
if (additions.length) lines.push("Lines to ADD:", ...additions, "");
|
|
22227
|
+
lines.push(`File: ${current.length} chars`);
|
|
22228
|
+
previewText = lines.join("\n");
|
|
22229
|
+
} else {
|
|
22230
|
+
previewText = [`\u{1F4CB} Preview: ${insight.targetFile}`, "", "(File will be created)", "", insight.proposedDiff].join("\n");
|
|
22231
|
+
}
|
|
22232
|
+
await sendOrEditKeyboard(
|
|
22233
|
+
chatId,
|
|
22234
|
+
channel,
|
|
22235
|
+
messageId,
|
|
22236
|
+
previewText,
|
|
22237
|
+
[[{ label: "\u2190 Back to Proposal", data: `evolve:review` }]]
|
|
22238
|
+
);
|
|
21533
22239
|
break;
|
|
21534
22240
|
}
|
|
21535
22241
|
case "stats": {
|
|
@@ -21538,46 +22244,49 @@ async function handleEvolveCallback(chatId, data, channel) {
|
|
|
21538
22244
|
const { calculateDrift: calculateDrift2 } = await Promise.resolve().then(() => (init_apply(), apply_exports));
|
|
21539
22245
|
const reportData = buildGrowthReportData2(getDb(), chatId, 30);
|
|
21540
22246
|
const modelData = buildModelPerformanceData2(getDb(), chatId, 30);
|
|
21541
|
-
|
|
22247
|
+
let report = formatGrowthReport2({
|
|
21542
22248
|
correctionsBefore: reportData.avgCorrectionsFirstHalf,
|
|
21543
22249
|
correctionsAfter: reportData.avgCorrectionsSecondHalf,
|
|
21544
22250
|
praiseRatio: reportData.praiseRatio,
|
|
21545
22251
|
insightsApplied: reportData.totalInsightsApplied,
|
|
21546
22252
|
pendingCount: reportData.pendingCount,
|
|
21547
|
-
topInsight: reportData.topInsightId != null ? {
|
|
21548
|
-
|
|
21549
|
-
effectiveness: reportData.topInsightEffectiveness ?? 0
|
|
21550
|
-
} : null
|
|
21551
|
-
};
|
|
21552
|
-
let report = formatGrowthReport2(metricsForReport, modelData);
|
|
22253
|
+
topInsight: reportData.topInsightId != null ? { insight: `#${reportData.topInsightId}`, effectiveness: reportData.topInsightEffectiveness ?? 0 } : null
|
|
22254
|
+
}, modelData);
|
|
21553
22255
|
const drift = calculateDrift2(chatId);
|
|
21554
22256
|
if (drift && (drift.soulDrift > 0.5 || drift.userDrift > 0.5)) {
|
|
21555
|
-
report += "\n\nSOUL.md has changed significantly since reflection started
|
|
22257
|
+
report += "\n\nSOUL.md has changed significantly since reflection started.";
|
|
21556
22258
|
}
|
|
21557
|
-
await
|
|
22259
|
+
await sendOrEditKeyboard(
|
|
22260
|
+
chatId,
|
|
22261
|
+
channel,
|
|
22262
|
+
messageId,
|
|
22263
|
+
report,
|
|
22264
|
+
[[{ label: "\u2190 Back to Dashboard", data: "evolve:menu" }]]
|
|
22265
|
+
);
|
|
21558
22266
|
break;
|
|
21559
22267
|
}
|
|
21560
22268
|
case "history": {
|
|
21561
22269
|
const { getAppliedInsights: getAppliedInsights2, getRejectedInsights: getRejectedInsights2 } = await Promise.resolve().then(() => (init_store4(), store_exports4));
|
|
21562
22270
|
const applied = getAppliedInsights2(getDb(), chatId, 10);
|
|
21563
22271
|
const rejected = getRejectedInsights2(getDb(), chatId, 10);
|
|
21564
|
-
|
|
21565
|
-
if (applied.length === 0)
|
|
21566
|
-
else applied.forEach((i) => {
|
|
21567
|
-
|
|
21568
|
-
|
|
21569
|
-
});
|
|
21570
|
-
|
|
21571
|
-
|
|
21572
|
-
|
|
21573
|
-
|
|
21574
|
-
|
|
21575
|
-
|
|
21576
|
-
|
|
22272
|
+
const lines = ["Applied Insights:", ""];
|
|
22273
|
+
if (applied.length === 0) lines.push("None yet.");
|
|
22274
|
+
else applied.forEach((i) => lines.push(`#${i.id} [${i.category}] ${i.insight}`));
|
|
22275
|
+
lines.push("", "Rejected Insights:", "");
|
|
22276
|
+
if (rejected.length === 0) lines.push("None yet.");
|
|
22277
|
+
else rejected.forEach((i) => lines.push(`#${i.id} [${i.category}] ${i.insight}`));
|
|
22278
|
+
await sendOrEditKeyboard(
|
|
22279
|
+
chatId,
|
|
22280
|
+
channel,
|
|
22281
|
+
messageId,
|
|
22282
|
+
lines.join("\n"),
|
|
22283
|
+
[[{ label: "\u2190 Back to Dashboard", data: "evolve:menu" }]]
|
|
22284
|
+
);
|
|
21577
22285
|
break;
|
|
21578
22286
|
}
|
|
21579
22287
|
case "toggle": {
|
|
21580
22288
|
const { getReflectionStatus: getReflectionStatus2, setReflectionStatus: setReflectionStatus2 } = await Promise.resolve().then(() => (init_store4(), store_exports4));
|
|
22289
|
+
const { logActivity: logActivity2 } = await Promise.resolve().then(() => (init_store3(), store_exports3));
|
|
21581
22290
|
const current = getReflectionStatus2(getDb(), chatId);
|
|
21582
22291
|
if (current === "frozen") {
|
|
21583
22292
|
const { readFileSync: readFileSync33, existsSync: existsSync62 } = await import("fs");
|
|
@@ -21588,14 +22297,13 @@ async function handleEvolveCallback(chatId, data, channel) {
|
|
|
21588
22297
|
const soul = existsSync62(soulPath) ? readFileSync33(soulPath, "utf-8") : "";
|
|
21589
22298
|
const user = existsSync62(userPath) ? readFileSync33(userPath, "utf-8") : "";
|
|
21590
22299
|
setReflectionStatus2(getDb(), chatId, "active", soul, user);
|
|
21591
|
-
const { logActivity: logActivity2 } = await Promise.resolve().then(() => (init_store3(), store_exports3));
|
|
21592
22300
|
logActivity2(getDb(), { chatId, source: "telegram", eventType: "reflection_unfrozen", summary: "Reflection enabled" });
|
|
21593
|
-
await
|
|
22301
|
+
await sendEvolveDashboard(chatId, reflChatId, channel, messageId);
|
|
21594
22302
|
} else {
|
|
21595
22303
|
setReflectionStatus2(getDb(), chatId, "frozen", void 0, void 0);
|
|
21596
|
-
const { logActivity: logActivity2 } = await Promise.resolve().then(() => (init_store3(), store_exports3));
|
|
21597
22304
|
logActivity2(getDb(), { chatId, source: "telegram", eventType: "reflection_frozen", summary: "Reflection disabled" });
|
|
21598
|
-
|
|
22305
|
+
const { text, buttons } = buildOnboardingView();
|
|
22306
|
+
await sendOrEditKeyboard(chatId, channel, messageId, text, buttons);
|
|
21599
22307
|
}
|
|
21600
22308
|
break;
|
|
21601
22309
|
}
|
|
@@ -21603,62 +22311,123 @@ async function handleEvolveCallback(chatId, data, channel) {
|
|
|
21603
22311
|
if (idStr) {
|
|
21604
22312
|
const { rollbackInsight: rollbackInsight2 } = await Promise.resolve().then(() => (init_apply(), apply_exports));
|
|
21605
22313
|
const result = await rollbackInsight2(parseInt(idStr, 10));
|
|
21606
|
-
await
|
|
22314
|
+
await sendOrEditKeyboard(
|
|
22315
|
+
chatId,
|
|
22316
|
+
channel,
|
|
22317
|
+
messageId,
|
|
22318
|
+
result.message,
|
|
22319
|
+
[[{ label: "\u2190 Back to Dashboard", data: "evolve:menu" }]]
|
|
22320
|
+
);
|
|
21607
22321
|
} else {
|
|
21608
22322
|
const { getAppliedInsights: getAppliedInsights2 } = await Promise.resolve().then(() => (init_store4(), store_exports4));
|
|
21609
|
-
const { buildUndoKeyboard: buildUndoKeyboard2 } = await Promise.resolve().then(() => (init_propose(), propose_exports));
|
|
21610
22323
|
const applied = getAppliedInsights2(getDb(), chatId, 10);
|
|
21611
22324
|
if (applied.length === 0) {
|
|
21612
|
-
await
|
|
22325
|
+
await sendOrEditKeyboard(
|
|
22326
|
+
chatId,
|
|
22327
|
+
channel,
|
|
22328
|
+
messageId,
|
|
22329
|
+
"No applied insights to undo.",
|
|
22330
|
+
[[{ label: "\u2190 Back to Dashboard", data: "evolve:menu" }]]
|
|
22331
|
+
);
|
|
21613
22332
|
} else {
|
|
21614
|
-
const
|
|
21615
|
-
|
|
22333
|
+
const buttons = [
|
|
22334
|
+
...applied.map((i) => [{ label: `Undo #${i.id}: ${i.insight}`, data: `evolve:undo:${i.id}`, style: "danger" }]),
|
|
22335
|
+
[{ label: "\u2190 Back to Dashboard", data: "evolve:menu" }]
|
|
22336
|
+
];
|
|
22337
|
+
await sendOrEditKeyboard(chatId, channel, messageId, "Select an insight to undo:", buttons);
|
|
21616
22338
|
}
|
|
21617
22339
|
}
|
|
21618
22340
|
break;
|
|
21619
22341
|
}
|
|
21620
22342
|
case "model": {
|
|
22343
|
+
const { getReflectionModelConfig: getReflectionModelConfig2, setReflectionModelConfig: setReflectionModelConfig2 } = await Promise.resolve().then(() => (init_store4(), store_exports4));
|
|
21621
22344
|
if (idStr) {
|
|
21622
|
-
const { setReflectionModelConfig: setReflectionModelConfig2 } = await Promise.resolve().then(() => (init_store4(), store_exports4));
|
|
21623
22345
|
setReflectionModelConfig2(getDb(), chatId, idStr);
|
|
21624
|
-
await
|
|
22346
|
+
await sendEvolveDashboard(chatId, reflChatId, channel, messageId);
|
|
21625
22347
|
} else {
|
|
21626
|
-
const { getReflectionModelConfig: getReflectionModelConfig2 } = await Promise.resolve().then(() => (init_store4(), store_exports4));
|
|
21627
|
-
const { buildModelKeyboard: buildModelKeyboard2 } = await Promise.resolve().then(() => (init_propose(), propose_exports));
|
|
21628
22348
|
const config2 = getReflectionModelConfig2(getDb(), chatId);
|
|
21629
|
-
const
|
|
21630
|
-
|
|
22349
|
+
const modes = [
|
|
22350
|
+
{ label: "Auto", mode: "auto" },
|
|
22351
|
+
{ label: "Cheap", mode: "cheap" },
|
|
22352
|
+
{ label: "Pinned", mode: "pinned" }
|
|
22353
|
+
];
|
|
22354
|
+
const buttons = [
|
|
22355
|
+
modes.map((m) => ({
|
|
22356
|
+
label: `${m.mode === config2.mode ? "\u2713 " : ""}${m.label}`,
|
|
22357
|
+
data: `evolve:model:${m.mode}`,
|
|
22358
|
+
...m.mode === config2.mode ? { style: "primary" } : {}
|
|
22359
|
+
})),
|
|
22360
|
+
[{ label: "\u2190 Back to Dashboard", data: "evolve:menu" }]
|
|
22361
|
+
];
|
|
22362
|
+
await sendOrEditKeyboard(
|
|
22363
|
+
chatId,
|
|
22364
|
+
channel,
|
|
22365
|
+
messageId,
|
|
22366
|
+
`Analysis model: ${config2.mode}`,
|
|
22367
|
+
buttons
|
|
22368
|
+
);
|
|
21631
22369
|
}
|
|
21632
22370
|
break;
|
|
21633
22371
|
}
|
|
21634
22372
|
}
|
|
21635
22373
|
}
|
|
21636
|
-
async function handleReflectCallback(chatId, data, channel) {
|
|
22374
|
+
async function handleReflectCallback(chatId, data, channel, messageId) {
|
|
21637
22375
|
const action = data.slice(8);
|
|
21638
22376
|
if (action === "go") {
|
|
21639
22377
|
const { runAnalysis: runAnalysis2 } = await Promise.resolve().then(() => (init_analyze(), analyze_exports));
|
|
21640
|
-
|
|
21641
|
-
|
|
22378
|
+
await sendOrEditKeyboard(
|
|
22379
|
+
chatId,
|
|
22380
|
+
channel,
|
|
22381
|
+
messageId,
|
|
22382
|
+
"\u{1F50D} Analyzing recent interactions...\n\nThis may take a moment.",
|
|
22383
|
+
[]
|
|
22384
|
+
);
|
|
21642
22385
|
try {
|
|
21643
22386
|
const insights = await runAnalysis2(chatId, { force: true });
|
|
21644
22387
|
if (insights.length === 0) {
|
|
21645
|
-
await
|
|
22388
|
+
await sendOrEditKeyboard(
|
|
22389
|
+
chatId,
|
|
22390
|
+
channel,
|
|
22391
|
+
messageId,
|
|
22392
|
+
"\u2705 Analysis complete.\n\nNo new insights from recent interactions.",
|
|
22393
|
+
[[{ label: "Review Dashboard", data: "evolve:menu" }]]
|
|
22394
|
+
);
|
|
21646
22395
|
} else {
|
|
21647
|
-
|
|
21648
|
-
|
|
22396
|
+
await sendOrEditKeyboard(
|
|
22397
|
+
chatId,
|
|
22398
|
+
channel,
|
|
22399
|
+
messageId,
|
|
22400
|
+
`\u2705 Analysis complete \u2014 ${insights.length} new proposal(s) ready.`,
|
|
22401
|
+
[
|
|
22402
|
+
[{ label: `Review (${insights.length})`, data: "evolve:review", style: "primary" }],
|
|
22403
|
+
[{ label: "Dashboard", data: "evolve:menu" }]
|
|
22404
|
+
]
|
|
22405
|
+
);
|
|
21649
22406
|
}
|
|
21650
22407
|
} catch (e) {
|
|
21651
|
-
await
|
|
22408
|
+
await sendOrEditKeyboard(
|
|
22409
|
+
chatId,
|
|
22410
|
+
channel,
|
|
22411
|
+
messageId,
|
|
22412
|
+
`\u274C Analysis failed: ${e}`,
|
|
22413
|
+
[[{ label: "Dashboard", data: "evolve:menu" }]]
|
|
22414
|
+
);
|
|
21652
22415
|
}
|
|
21653
22416
|
} else if (action === "cancel") {
|
|
21654
|
-
await
|
|
22417
|
+
await sendOrEditKeyboard(
|
|
22418
|
+
chatId,
|
|
22419
|
+
channel,
|
|
22420
|
+
messageId,
|
|
22421
|
+
"Reflection cancelled.",
|
|
22422
|
+
[[{ label: "Dashboard", data: "evolve:menu" }]]
|
|
22423
|
+
);
|
|
21655
22424
|
}
|
|
21656
22425
|
}
|
|
21657
22426
|
var init_evolve2 = __esm({
|
|
21658
22427
|
"src/router/evolve.ts"() {
|
|
21659
22428
|
"use strict";
|
|
21660
22429
|
init_store5();
|
|
21661
|
-
|
|
22430
|
+
init_helpers();
|
|
21662
22431
|
}
|
|
21663
22432
|
});
|
|
21664
22433
|
|
|
@@ -22304,8 +23073,8 @@ var init_analyze2 = __esm({
|
|
|
22304
23073
|
});
|
|
22305
23074
|
|
|
22306
23075
|
// src/optimizer/ui.ts
|
|
22307
|
-
var
|
|
22308
|
-
__export(
|
|
23076
|
+
var ui_exports2 = {};
|
|
23077
|
+
__export(ui_exports2, {
|
|
22309
23078
|
buildFindingKeyboard: () => buildFindingKeyboard,
|
|
22310
23079
|
buildFindingMessage: () => buildFindingMessage,
|
|
22311
23080
|
buildIdentityAuditKeyboard: () => buildIdentityAuditKeyboard,
|
|
@@ -22588,14 +23357,14 @@ __export(optimize_exports, {
|
|
|
22588
23357
|
import { readFileSync as readFileSync17, writeFileSync as writeFileSync9, existsSync as existsSync28, readdirSync as readdirSync13, unlinkSync as unlinkSync7 } from "fs";
|
|
22589
23358
|
import { join as join29, dirname as dirname4 } from "path";
|
|
22590
23359
|
import { homedir as homedir8 } from "os";
|
|
22591
|
-
async function handleOptimizeCommand(chatId, channel, _args) {
|
|
23360
|
+
async function handleOptimizeCommand(chatId, channel, _args, messageId) {
|
|
22592
23361
|
const { getModelDisplayInfo: getModelDisplayInfo2 } = await Promise.resolve().then(() => (init_analyze2(), analyze_exports2));
|
|
22593
23362
|
const {
|
|
22594
23363
|
buildMainMenuMessage: buildMainMenuMessage2,
|
|
22595
23364
|
buildMainMenuKeyboard: buildMainMenuKeyboard2,
|
|
22596
23365
|
buildModelRecommendationMessage: buildModelRecommendationMessage2,
|
|
22597
23366
|
buildModelRecommendationKeyboard: buildModelRecommendationKeyboard2
|
|
22598
|
-
} = await Promise.resolve().then(() => (init_ui2(),
|
|
23367
|
+
} = await Promise.resolve().then(() => (init_ui2(), ui_exports2));
|
|
22599
23368
|
const modelInfo = getModelDisplayInfo2(chatId);
|
|
22600
23369
|
if (!modelInfo) {
|
|
22601
23370
|
await channel.sendText(chatId, "No AI backend available. Configure one first.", { parseMode: "plain" });
|
|
@@ -22603,105 +23372,106 @@ async function handleOptimizeCommand(chatId, channel, _args) {
|
|
|
22603
23372
|
}
|
|
22604
23373
|
if (modelInfo.isWeak) {
|
|
22605
23374
|
const msg2 = buildModelRecommendationMessage2(modelInfo.model);
|
|
22606
|
-
|
|
22607
|
-
await channel.sendKeyboard(chatId, msg2, buildModelRecommendationKeyboard2());
|
|
22608
|
-
} else {
|
|
22609
|
-
await channel.sendText(chatId, msg2 + "\n\nSwitch to an advanced model for best results.", { parseMode: "plain" });
|
|
22610
|
-
}
|
|
23375
|
+
await sendOrEditKeyboard(chatId, channel, messageId, msg2, buildModelRecommendationKeyboard2());
|
|
22611
23376
|
return;
|
|
22612
23377
|
}
|
|
22613
23378
|
const msg = buildMainMenuMessage2(modelInfo.backend, modelInfo.model, modelInfo.thinkingLevel);
|
|
22614
|
-
|
|
22615
|
-
await channel.sendKeyboard(chatId, msg, buildMainMenuKeyboard2());
|
|
22616
|
-
} else {
|
|
22617
|
-
await channel.sendText(chatId, msg, { parseMode: "plain" });
|
|
22618
|
-
}
|
|
23379
|
+
await sendOrEditKeyboard(chatId, channel, messageId, msg, buildMainMenuKeyboard2());
|
|
22619
23380
|
}
|
|
22620
|
-
async function handleOptimizeCallback(chatId, data, channel) {
|
|
23381
|
+
async function handleOptimizeCallback(chatId, data, channel, messageId) {
|
|
22621
23382
|
const parts = data.split(":");
|
|
22622
23383
|
const action = parts[1];
|
|
22623
23384
|
switch (action) {
|
|
22624
23385
|
case "identity": {
|
|
22625
|
-
await runIdentityAuditFlow(chatId, channel);
|
|
23386
|
+
await runIdentityAuditFlow(chatId, channel, messageId);
|
|
22626
23387
|
break;
|
|
22627
23388
|
}
|
|
22628
23389
|
case "skill-menu": {
|
|
22629
|
-
await showSkillPicker(chatId, channel, 0);
|
|
23390
|
+
await showSkillPicker(chatId, channel, 0, messageId);
|
|
22630
23391
|
break;
|
|
22631
23392
|
}
|
|
22632
23393
|
case "skill-page": {
|
|
22633
23394
|
const page = parseInt(parts[2], 10) || 0;
|
|
22634
|
-
await showSkillPicker(chatId, channel, page);
|
|
23395
|
+
await showSkillPicker(chatId, channel, page, messageId);
|
|
22635
23396
|
break;
|
|
22636
23397
|
}
|
|
22637
23398
|
case "skill-pick": {
|
|
22638
23399
|
const skillName = parts.slice(2).join(":");
|
|
22639
|
-
await runSkillAuditFlow(chatId, channel, skillName);
|
|
23400
|
+
await runSkillAuditFlow(chatId, channel, skillName, messageId);
|
|
22640
23401
|
break;
|
|
22641
23402
|
}
|
|
22642
23403
|
case "model-ok": {
|
|
22643
23404
|
const { getModelDisplayInfo: getModelDisplayInfo2 } = await Promise.resolve().then(() => (init_analyze2(), analyze_exports2));
|
|
22644
|
-
const { buildMainMenuMessage: buildMainMenuMessage2, buildMainMenuKeyboard: buildMainMenuKeyboard2 } = await Promise.resolve().then(() => (init_ui2(),
|
|
23405
|
+
const { buildMainMenuMessage: buildMainMenuMessage2, buildMainMenuKeyboard: buildMainMenuKeyboard2 } = await Promise.resolve().then(() => (init_ui2(), ui_exports2));
|
|
22645
23406
|
const modelInfo = getModelDisplayInfo2(chatId);
|
|
22646
23407
|
if (!modelInfo) return;
|
|
22647
23408
|
const msg = buildMainMenuMessage2(modelInfo.backend, modelInfo.model, modelInfo.thinkingLevel);
|
|
22648
|
-
|
|
22649
|
-
await channel.sendKeyboard(chatId, msg, buildMainMenuKeyboard2());
|
|
22650
|
-
}
|
|
23409
|
+
await sendOrEditKeyboard(chatId, channel, messageId, msg, buildMainMenuKeyboard2());
|
|
22651
23410
|
break;
|
|
22652
23411
|
}
|
|
22653
23412
|
case "model-switch": {
|
|
22654
|
-
await
|
|
23413
|
+
await sendOrEditKeyboard(
|
|
23414
|
+
chatId,
|
|
23415
|
+
channel,
|
|
23416
|
+
messageId,
|
|
23417
|
+
"Use /model or /backend to switch to a more powerful model, then run /optimize again.",
|
|
23418
|
+
[[{ label: "\u2190 Back", data: "opt:model-ok" }]]
|
|
23419
|
+
);
|
|
22655
23420
|
break;
|
|
22656
23421
|
}
|
|
22657
23422
|
case "review": {
|
|
22658
23423
|
const index = parseInt(parts[2], 10) || 0;
|
|
22659
|
-
|
|
23424
|
+
const session2 = activeSessions.get(chatId);
|
|
23425
|
+
if (session2 && messageId) session2.messageId = messageId;
|
|
23426
|
+
await showFinding(chatId, channel, index, messageId);
|
|
22660
23427
|
break;
|
|
22661
23428
|
}
|
|
22662
23429
|
case "fix": {
|
|
22663
23430
|
const fixIndex = parseInt(parts[2], 10) || 0;
|
|
22664
|
-
|
|
23431
|
+
const session2 = activeSessions.get(chatId);
|
|
23432
|
+
await applyFinding(chatId, channel, fixIndex, messageId ?? session2?.messageId);
|
|
22665
23433
|
break;
|
|
22666
23434
|
}
|
|
22667
23435
|
case "skip": {
|
|
22668
23436
|
const skipIndex = parseInt(parts[2], 10) || 0;
|
|
22669
|
-
|
|
23437
|
+
const session2 = activeSessions.get(chatId);
|
|
23438
|
+
await skipFinding(chatId, channel, skipIndex, messageId ?? session2?.messageId);
|
|
22670
23439
|
break;
|
|
22671
23440
|
}
|
|
22672
23441
|
case "stop": {
|
|
22673
|
-
|
|
23442
|
+
const session2 = activeSessions.get(chatId);
|
|
23443
|
+
await stopReview(chatId, channel, messageId ?? session2?.messageId);
|
|
22674
23444
|
break;
|
|
22675
23445
|
}
|
|
22676
23446
|
case "close": {
|
|
22677
23447
|
activeSessions.delete(chatId);
|
|
22678
|
-
|
|
23448
|
+
if (messageId && channel.editText) {
|
|
23449
|
+
await channel.editText(chatId, messageId, "Optimizer closed.", "plain");
|
|
23450
|
+
} else {
|
|
23451
|
+
await channel.sendText(chatId, "Optimizer closed.", { parseMode: "plain" });
|
|
23452
|
+
}
|
|
22679
23453
|
break;
|
|
22680
23454
|
}
|
|
22681
23455
|
default:
|
|
22682
23456
|
break;
|
|
22683
23457
|
}
|
|
22684
23458
|
}
|
|
22685
|
-
async function runIdentityAuditFlow(chatId, channel) {
|
|
23459
|
+
async function runIdentityAuditFlow(chatId, channel, messageId) {
|
|
22686
23460
|
const { getModelDisplayInfo: getModelDisplayInfo2, runIdentityAudit: runIdentityAudit2 } = await Promise.resolve().then(() => (init_analyze2(), analyze_exports2));
|
|
22687
23461
|
const {
|
|
22688
23462
|
buildProgressMessage: buildProgressMessage2,
|
|
22689
23463
|
buildIdentityAuditSummary: buildIdentityAuditSummary2,
|
|
22690
23464
|
buildIdentityAuditKeyboard: buildIdentityAuditKeyboard2
|
|
22691
|
-
} = await Promise.resolve().then(() => (init_ui2(),
|
|
23465
|
+
} = await Promise.resolve().then(() => (init_ui2(), ui_exports2));
|
|
22692
23466
|
const modelInfo = getModelDisplayInfo2(chatId);
|
|
22693
23467
|
if (!modelInfo) return;
|
|
22694
|
-
const
|
|
22695
|
-
|
|
22696
|
-
|
|
22697
|
-
"
|
|
22698
|
-
|
|
22699
|
-
|
|
22700
|
-
await channel.sendText(
|
|
22701
|
-
chatId,
|
|
22702
|
-
buildProgressMessage2("identity files", modelInfo.backend, modelInfo.model, modelInfo.thinkingLevel),
|
|
22703
|
-
{ parseMode: "plain" }
|
|
22704
|
-
);
|
|
23468
|
+
const progressText = buildProgressMessage2("identity files", modelInfo.backend, modelInfo.model, modelInfo.thinkingLevel);
|
|
23469
|
+
let progressMsgId = messageId;
|
|
23470
|
+
if (messageId && channel.editKeyboard) {
|
|
23471
|
+
await channel.editKeyboard(chatId, messageId, progressText + "\n\u23F3 Analyzing...", []);
|
|
23472
|
+
} else {
|
|
23473
|
+
progressMsgId = await channel.sendTextReturningId?.(chatId, progressText, "plain");
|
|
23474
|
+
if (!progressMsgId) await channel.sendText(chatId, progressText, { parseMode: "plain" });
|
|
22705
23475
|
}
|
|
22706
23476
|
const startTime = Date.now();
|
|
22707
23477
|
const progressInterval = setInterval(async () => {
|
|
@@ -22709,13 +23479,8 @@ async function runIdentityAuditFlow(chatId, channel) {
|
|
|
22709
23479
|
try {
|
|
22710
23480
|
if (channel.sendTyping) await channel.sendTyping(chatId);
|
|
22711
23481
|
if (progressMsgId && channel.editText) {
|
|
22712
|
-
await channel.editText(
|
|
22713
|
-
|
|
22714
|
-
progressMsgId,
|
|
22715
|
-
buildProgressMessage2("identity files", modelInfo.backend, modelInfo.model, modelInfo.thinkingLevel) + `
|
|
22716
|
-
\u23F3 Analyzing... (${elapsed}s)`,
|
|
22717
|
-
"plain"
|
|
22718
|
-
);
|
|
23482
|
+
await channel.editText(chatId, progressMsgId, progressText + `
|
|
23483
|
+
\u23F3 Analyzing... (${elapsed}s)`, "plain");
|
|
22719
23484
|
}
|
|
22720
23485
|
} catch {
|
|
22721
23486
|
}
|
|
@@ -22723,62 +23488,43 @@ async function runIdentityAuditFlow(chatId, channel) {
|
|
|
22723
23488
|
try {
|
|
22724
23489
|
const result = await runIdentityAudit2(chatId);
|
|
22725
23490
|
clearInterval(progressInterval);
|
|
22726
|
-
activeSessions.set(chatId, {
|
|
22727
|
-
chatId,
|
|
22728
|
-
result,
|
|
22729
|
-
currentIndex: 0,
|
|
22730
|
-
applied: [],
|
|
22731
|
-
skipped: []
|
|
22732
|
-
});
|
|
23491
|
+
activeSessions.set(chatId, { chatId, result, currentIndex: 0, applied: [], skipped: [], messageId: progressMsgId });
|
|
22733
23492
|
const summary = buildIdentityAuditSummary2(result);
|
|
22734
|
-
|
|
22735
|
-
await channel.sendKeyboard(chatId, summary, buildIdentityAuditKeyboard2(result.findings.length));
|
|
22736
|
-
} else {
|
|
22737
|
-
await channel.sendText(chatId, summary, { parseMode: "plain" });
|
|
22738
|
-
}
|
|
23493
|
+
await sendOrEditKeyboard(chatId, channel, progressMsgId, summary, buildIdentityAuditKeyboard2(result.findings.length));
|
|
22739
23494
|
} catch (e) {
|
|
22740
23495
|
clearInterval(progressInterval);
|
|
22741
|
-
await
|
|
23496
|
+
await sendOrEditKeyboard(chatId, channel, progressMsgId, `Identity audit failed: ${e}`, [[{ label: "\u2190 Back", data: "opt:model-ok" }]]);
|
|
22742
23497
|
}
|
|
22743
23498
|
}
|
|
22744
|
-
async function showSkillPicker(chatId, channel, page) {
|
|
23499
|
+
async function showSkillPicker(chatId, channel, page, messageId) {
|
|
22745
23500
|
const { listCcClawSkills: listCcClawSkills2 } = await Promise.resolve().then(() => (init_analyze2(), analyze_exports2));
|
|
22746
|
-
const { buildSkillPickerMessage: buildSkillPickerMessage2, buildSkillPickerKeyboard: buildSkillPickerKeyboard2 } = await Promise.resolve().then(() => (init_ui2(),
|
|
23501
|
+
const { buildSkillPickerMessage: buildSkillPickerMessage2, buildSkillPickerKeyboard: buildSkillPickerKeyboard2 } = await Promise.resolve().then(() => (init_ui2(), ui_exports2));
|
|
22747
23502
|
const skills2 = listCcClawSkills2();
|
|
22748
23503
|
if (skills2.length === 0) {
|
|
22749
|
-
await
|
|
23504
|
+
await sendOrEditKeyboard(chatId, channel, messageId, "No CC-Claw skills found in ~/.cc-claw/workspace/skills/", [[{ label: "\u2190 Back", data: "opt:model-ok" }]]);
|
|
22750
23505
|
return;
|
|
22751
23506
|
}
|
|
22752
23507
|
const totalPages = Math.ceil(skills2.length / 6);
|
|
22753
23508
|
const msg = buildSkillPickerMessage2(page, totalPages);
|
|
22754
|
-
|
|
22755
|
-
await channel.sendKeyboard(chatId, msg, buildSkillPickerKeyboard2(skills2, page));
|
|
22756
|
-
} else {
|
|
22757
|
-
const list = skills2.map((s) => `\u2022 ${s.name} \u2014 ${s.description}`).join("\n");
|
|
22758
|
-
await channel.sendText(chatId, msg + "\n\n" + list, { parseMode: "plain" });
|
|
22759
|
-
}
|
|
23509
|
+
await sendOrEditKeyboard(chatId, channel, messageId, msg, buildSkillPickerKeyboard2(skills2, page));
|
|
22760
23510
|
}
|
|
22761
|
-
async function runSkillAuditFlow(chatId, channel, skillName) {
|
|
23511
|
+
async function runSkillAuditFlow(chatId, channel, skillName, messageId) {
|
|
22762
23512
|
const { getModelDisplayInfo: getModelDisplayInfo2, runSkillAudit: runSkillAudit2 } = await Promise.resolve().then(() => (init_analyze2(), analyze_exports2));
|
|
22763
23513
|
const {
|
|
22764
23514
|
buildProgressMessage: buildProgressMessage2,
|
|
22765
23515
|
buildSkillAuditSummary: buildSkillAuditSummary2,
|
|
22766
23516
|
buildSkillAuditKeyboard: buildSkillAuditKeyboard2
|
|
22767
|
-
} = await Promise.resolve().then(() => (init_ui2(),
|
|
23517
|
+
} = await Promise.resolve().then(() => (init_ui2(), ui_exports2));
|
|
22768
23518
|
const modelInfo = getModelDisplayInfo2(chatId);
|
|
22769
23519
|
if (!modelInfo) return;
|
|
22770
23520
|
const skillPath = join29(homedir8(), ".cc-claw", "workspace", "skills", skillName, "SKILL.md");
|
|
22771
|
-
const
|
|
22772
|
-
|
|
22773
|
-
|
|
22774
|
-
"
|
|
22775
|
-
|
|
22776
|
-
|
|
22777
|
-
await channel.sendText(
|
|
22778
|
-
chatId,
|
|
22779
|
-
buildProgressMessage2(`skill: ${skillName}`, modelInfo.backend, modelInfo.model, modelInfo.thinkingLevel),
|
|
22780
|
-
{ parseMode: "plain" }
|
|
22781
|
-
);
|
|
23521
|
+
const progressText = buildProgressMessage2(`skill: ${skillName}`, modelInfo.backend, modelInfo.model, modelInfo.thinkingLevel);
|
|
23522
|
+
let progressMsgId = messageId;
|
|
23523
|
+
if (messageId && channel.editKeyboard) {
|
|
23524
|
+
await channel.editKeyboard(chatId, messageId, progressText + "\n\u23F3 Analyzing...", []);
|
|
23525
|
+
} else {
|
|
23526
|
+
progressMsgId = await channel.sendTextReturningId?.(chatId, progressText, "plain");
|
|
23527
|
+
if (!progressMsgId) await channel.sendText(chatId, progressText, { parseMode: "plain" });
|
|
22782
23528
|
}
|
|
22783
23529
|
const startTime = Date.now();
|
|
22784
23530
|
const progressInterval = setInterval(async () => {
|
|
@@ -22786,13 +23532,8 @@ async function runSkillAuditFlow(chatId, channel, skillName) {
|
|
|
22786
23532
|
try {
|
|
22787
23533
|
if (channel.sendTyping) await channel.sendTyping(chatId);
|
|
22788
23534
|
if (progressMsgId && channel.editText) {
|
|
22789
|
-
await channel.editText(
|
|
22790
|
-
|
|
22791
|
-
progressMsgId,
|
|
22792
|
-
buildProgressMessage2(`skill: ${skillName}`, modelInfo.backend, modelInfo.model, modelInfo.thinkingLevel) + `
|
|
22793
|
-
\u23F3 Analyzing... (${elapsed}s)`,
|
|
22794
|
-
"plain"
|
|
22795
|
-
);
|
|
23535
|
+
await channel.editText(chatId, progressMsgId, progressText + `
|
|
23536
|
+
\u23F3 Analyzing... (${elapsed}s)`, "plain");
|
|
22796
23537
|
}
|
|
22797
23538
|
} catch {
|
|
22798
23539
|
}
|
|
@@ -22800,66 +23541,47 @@ async function runSkillAuditFlow(chatId, channel, skillName) {
|
|
|
22800
23541
|
try {
|
|
22801
23542
|
const result = await runSkillAudit2(chatId, skillPath);
|
|
22802
23543
|
clearInterval(progressInterval);
|
|
22803
|
-
activeSessions.set(chatId, {
|
|
22804
|
-
chatId,
|
|
22805
|
-
result,
|
|
22806
|
-
currentIndex: 0,
|
|
22807
|
-
applied: [],
|
|
22808
|
-
skipped: []
|
|
22809
|
-
});
|
|
23544
|
+
activeSessions.set(chatId, { chatId, result, currentIndex: 0, applied: [], skipped: [], messageId: progressMsgId });
|
|
22810
23545
|
const summary = buildSkillAuditSummary2(result);
|
|
22811
|
-
|
|
22812
|
-
await channel.sendKeyboard(chatId, summary, buildSkillAuditKeyboard2(result.findings.length));
|
|
22813
|
-
} else {
|
|
22814
|
-
await channel.sendText(chatId, summary, { parseMode: "plain" });
|
|
22815
|
-
}
|
|
23546
|
+
await sendOrEditKeyboard(chatId, channel, progressMsgId, summary, buildSkillAuditKeyboard2(result.findings.length));
|
|
22816
23547
|
} catch (e) {
|
|
22817
23548
|
clearInterval(progressInterval);
|
|
22818
|
-
await
|
|
23549
|
+
await sendOrEditKeyboard(chatId, channel, progressMsgId, `Skill audit failed: ${e}`, [[{ label: "\u2190 Back", data: "opt:skill-menu" }]]);
|
|
22819
23550
|
}
|
|
22820
23551
|
}
|
|
22821
|
-
async function showFinding(chatId, channel, index) {
|
|
23552
|
+
async function showFinding(chatId, channel, index, messageId) {
|
|
22822
23553
|
const session2 = activeSessions.get(chatId);
|
|
22823
23554
|
if (!session2) {
|
|
22824
|
-
await
|
|
23555
|
+
await sendOrEditKeyboard(chatId, channel, messageId, "No active optimizer session. Run /optimize first.", [[{ label: "Run Optimizer", data: "opt:model-ok" }]]);
|
|
22825
23556
|
return;
|
|
22826
23557
|
}
|
|
23558
|
+
const effectiveMsgId = messageId ?? session2.messageId;
|
|
22827
23559
|
const { findings } = session2.result;
|
|
22828
23560
|
if (index >= findings.length) {
|
|
22829
|
-
await finishReview(chatId, channel);
|
|
23561
|
+
await finishReview(chatId, channel, effectiveMsgId);
|
|
22830
23562
|
return;
|
|
22831
23563
|
}
|
|
22832
23564
|
session2.currentIndex = index;
|
|
23565
|
+
if (effectiveMsgId) session2.messageId = effectiveMsgId;
|
|
22833
23566
|
const finding = findings[index];
|
|
22834
|
-
const { buildFindingMessage: buildFindingMessage2, buildFindingKeyboard: buildFindingKeyboard2 } = await Promise.resolve().then(() => (init_ui2(),
|
|
23567
|
+
const { buildFindingMessage: buildFindingMessage2, buildFindingKeyboard: buildFindingKeyboard2 } = await Promise.resolve().then(() => (init_ui2(), ui_exports2));
|
|
22835
23568
|
const msg = buildFindingMessage2(finding, index, findings.length);
|
|
22836
|
-
|
|
22837
|
-
await channel.sendKeyboard(chatId, msg, buildFindingKeyboard2(index, findings.length, !!finding.proposedDiff));
|
|
22838
|
-
} else {
|
|
22839
|
-
await channel.sendText(chatId, msg, { parseMode: "plain" });
|
|
22840
|
-
}
|
|
23569
|
+
await sendOrEditKeyboard(chatId, channel, effectiveMsgId, msg, buildFindingKeyboard2(index, findings.length, !!finding.proposedDiff));
|
|
22841
23570
|
}
|
|
22842
|
-
async function applyFinding(chatId, channel, index) {
|
|
23571
|
+
async function applyFinding(chatId, channel, index, messageId) {
|
|
22843
23572
|
const session2 = activeSessions.get(chatId);
|
|
22844
23573
|
if (!session2) return;
|
|
23574
|
+
const effectiveMsgId = messageId ?? session2.messageId;
|
|
22845
23575
|
const finding = session2.result.findings[index];
|
|
22846
23576
|
if (!finding || !finding.proposedDiff) {
|
|
22847
|
-
await
|
|
22848
|
-
await showFinding(chatId, channel, index + 1);
|
|
23577
|
+
await showFinding(chatId, channel, index + 1, effectiveMsgId);
|
|
22849
23578
|
return;
|
|
22850
23579
|
}
|
|
22851
23580
|
try {
|
|
22852
23581
|
const targetPath = resolveTargetFile(finding.location, session2.result.target);
|
|
22853
|
-
if (!targetPath) {
|
|
22854
|
-
await channel.sendText(chatId, `Cannot determine target file from location: ${finding.location}`, { parseMode: "plain" });
|
|
23582
|
+
if (!targetPath || !existsSync28(targetPath)) {
|
|
22855
23583
|
session2.skipped.push(index);
|
|
22856
|
-
await showFinding(chatId, channel, index + 1);
|
|
22857
|
-
return;
|
|
22858
|
-
}
|
|
22859
|
-
if (!existsSync28(targetPath)) {
|
|
22860
|
-
await channel.sendText(chatId, `Target file not found: ${targetPath}`, { parseMode: "plain" });
|
|
22861
|
-
session2.skipped.push(index);
|
|
22862
|
-
await showFinding(chatId, channel, index + 1);
|
|
23584
|
+
await showFinding(chatId, channel, index + 1, effectiveMsgId);
|
|
22863
23585
|
return;
|
|
22864
23586
|
}
|
|
22865
23587
|
const original = readFileSync17(targetPath, "utf-8");
|
|
@@ -22869,13 +23591,7 @@ async function applyFinding(chatId, channel, index) {
|
|
|
22869
23591
|
let newContent;
|
|
22870
23592
|
try {
|
|
22871
23593
|
const { applyWithAI: applyWithAI2 } = await Promise.resolve().then(() => (init_ai_apply(), ai_apply_exports));
|
|
22872
|
-
const aiResult = await applyWithAI2(
|
|
22873
|
-
chatId,
|
|
22874
|
-
original,
|
|
22875
|
-
finding.suggestion || finding.title,
|
|
22876
|
-
finding.proposedDiff,
|
|
22877
|
-
finding.location.split(":")[0] || "unknown"
|
|
22878
|
-
);
|
|
23594
|
+
const aiResult = await applyWithAI2(chatId, original, finding.suggestion || finding.title, finding.proposedDiff, finding.location.split(":")[0] || "unknown");
|
|
22879
23595
|
if (aiResult.success) {
|
|
22880
23596
|
newContent = aiResult.newContent;
|
|
22881
23597
|
} else {
|
|
@@ -22889,9 +23605,8 @@ async function applyFinding(chatId, channel, index) {
|
|
|
22889
23605
|
newContent = applyDiff2(original, finding.proposedDiff, "replace");
|
|
22890
23606
|
}
|
|
22891
23607
|
if (newContent === original) {
|
|
22892
|
-
await channel.sendText(chatId, `\u26A0\uFE0F Diff produced no changes. The content may have already been modified.`, { parseMode: "plain" });
|
|
22893
23608
|
session2.skipped.push(index);
|
|
22894
|
-
await showFinding(chatId, channel, index + 1);
|
|
23609
|
+
await showFinding(chatId, channel, index + 1, effectiveMsgId);
|
|
22895
23610
|
return;
|
|
22896
23611
|
}
|
|
22897
23612
|
writeFileSync9(targetPath, newContent, "utf-8");
|
|
@@ -22904,34 +23619,32 @@ async function applyFinding(chatId, channel, index) {
|
|
|
22904
23619
|
warn("[optimize] syncNativeCliFiles failed:", err instanceof Error ? err.message : err);
|
|
22905
23620
|
}
|
|
22906
23621
|
}
|
|
22907
|
-
await channel.sendText(chatId, `\u2705 Applied: ${finding.title}`, { parseMode: "plain" });
|
|
22908
23622
|
} catch (e) {
|
|
22909
|
-
const errorMsg = e?.message ?? String(e);
|
|
22910
|
-
await channel.sendText(chatId, `\u274C Failed to apply fix: ${errorMsg}`, { parseMode: "plain" });
|
|
22911
23623
|
session2.skipped.push(index);
|
|
22912
23624
|
}
|
|
22913
|
-
await showFinding(chatId, channel, index + 1);
|
|
23625
|
+
await showFinding(chatId, channel, index + 1, effectiveMsgId);
|
|
22914
23626
|
}
|
|
22915
|
-
async function skipFinding(chatId, channel, index) {
|
|
23627
|
+
async function skipFinding(chatId, channel, index, messageId) {
|
|
22916
23628
|
const session2 = activeSessions.get(chatId);
|
|
22917
23629
|
if (!session2) return;
|
|
22918
23630
|
session2.skipped.push(index);
|
|
22919
|
-
await showFinding(chatId, channel, index + 1);
|
|
23631
|
+
await showFinding(chatId, channel, index + 1, messageId ?? session2.messageId);
|
|
22920
23632
|
}
|
|
22921
|
-
async function stopReview(chatId, channel) {
|
|
22922
|
-
await finishReview(chatId, channel);
|
|
23633
|
+
async function stopReview(chatId, channel, messageId) {
|
|
23634
|
+
await finishReview(chatId, channel, messageId);
|
|
22923
23635
|
}
|
|
22924
|
-
async function finishReview(chatId, channel) {
|
|
23636
|
+
async function finishReview(chatId, channel, messageId) {
|
|
22925
23637
|
const session2 = activeSessions.get(chatId);
|
|
22926
23638
|
if (!session2) return;
|
|
22927
|
-
const { buildReviewCompleteMessage: buildReviewCompleteMessage3, buildReviewCompleteKeyboard: buildReviewCompleteKeyboard2 } = await Promise.resolve().then(() => (init_ui2(),
|
|
23639
|
+
const { buildReviewCompleteMessage: buildReviewCompleteMessage3, buildReviewCompleteKeyboard: buildReviewCompleteKeyboard2 } = await Promise.resolve().then(() => (init_ui2(), ui_exports2));
|
|
22928
23640
|
const msg = buildReviewCompleteMessage3(
|
|
22929
23641
|
session2.applied.length,
|
|
22930
23642
|
session2.skipped.length,
|
|
22931
23643
|
session2.result.findings.length
|
|
22932
23644
|
);
|
|
22933
|
-
|
|
22934
|
-
|
|
23645
|
+
const effectiveMsgId = messageId ?? session2.messageId;
|
|
23646
|
+
if (effectiveMsgId) {
|
|
23647
|
+
await sendOrEditKeyboard(chatId, channel, effectiveMsgId, msg, buildReviewCompleteKeyboard2());
|
|
22935
23648
|
} else {
|
|
22936
23649
|
await channel.sendText(chatId, msg, { parseMode: "plain" });
|
|
22937
23650
|
}
|
|
@@ -22971,6 +23684,7 @@ var init_optimize = __esm({
|
|
|
22971
23684
|
"src/router/optimize.ts"() {
|
|
22972
23685
|
"use strict";
|
|
22973
23686
|
init_log();
|
|
23687
|
+
init_helpers();
|
|
22974
23688
|
activeSessions = /* @__PURE__ */ new Map();
|
|
22975
23689
|
}
|
|
22976
23690
|
});
|
|
@@ -23196,7 +23910,7 @@ __export(auto_create_exports, {
|
|
|
23196
23910
|
storePendingDraft: () => storePendingDraft
|
|
23197
23911
|
});
|
|
23198
23912
|
import { join as join30 } from "path";
|
|
23199
|
-
import { writeFile as writeFile5, mkdir as
|
|
23913
|
+
import { writeFile as writeFile5, mkdir as mkdir4 } from "fs/promises";
|
|
23200
23914
|
function isSkillWorthy(signals) {
|
|
23201
23915
|
const { toolUseCount, tokenOutput, elapsedMs, userMessage } = signals;
|
|
23202
23916
|
if (toolUseCount < 12) return false;
|
|
@@ -23378,7 +24092,7 @@ async function saveSkill(name, content, opts = {}) {
|
|
|
23378
24092
|
}
|
|
23379
24093
|
}
|
|
23380
24094
|
const dir = join30(SKILLS_PATH, name);
|
|
23381
|
-
await
|
|
24095
|
+
await mkdir4(dir, { recursive: true });
|
|
23382
24096
|
const filePath = join30(dir, "SKILL.md");
|
|
23383
24097
|
await writeFile5(filePath, withFrontmatter, "utf-8");
|
|
23384
24098
|
invalidateSkillCache();
|
|
@@ -23558,34 +24272,10 @@ async function handleVoiceConfigCommand(chatId, commandArgs, msg, channel) {
|
|
|
23558
24272
|
await sendVoiceConfigKeyboard(chatId, channel);
|
|
23559
24273
|
}
|
|
23560
24274
|
async function handleResponseStyleCommand(chatId, commandArgs, msg, channel) {
|
|
23561
|
-
|
|
23562
|
-
if (typeof channel.sendKeyboard === "function") {
|
|
23563
|
-
await channel.sendKeyboard(chatId, "\u{1F5E3}\uFE0F AI Response Style:", [
|
|
23564
|
-
[
|
|
23565
|
-
{ label: `${currentStyle === "concise" ? "\u2713 " : ""}Concise`, data: "style:concise", ...currentStyle === "concise" ? { style: "primary" } : {} },
|
|
23566
|
-
{ label: `${currentStyle === "normal" ? "\u2713 " : ""}Normal`, data: "style:normal", ...currentStyle === "normal" ? { style: "primary" } : {} },
|
|
23567
|
-
{ label: `${currentStyle === "detailed" ? "\u2713 " : ""}Detailed`, data: "style:detailed", ...currentStyle === "detailed" ? { style: "primary" } : {} }
|
|
23568
|
-
]
|
|
23569
|
-
]);
|
|
23570
|
-
} else {
|
|
23571
|
-
await channel.sendText(chatId, `Current Response Style: ${currentStyle}`, { parseMode: "plain" });
|
|
23572
|
-
}
|
|
24275
|
+
await sendResponseStyleKeyboard(chatId, channel);
|
|
23573
24276
|
}
|
|
23574
24277
|
async function handleModelSignatureCommand(chatId, commandArgs, msg, channel) {
|
|
23575
|
-
|
|
23576
|
-
if (typeof channel.sendKeyboard === "function") {
|
|
23577
|
-
await channel.sendKeyboard(chatId, `Model Signature: ${currentSig === "on" ? "ON" : "OFF"}
|
|
23578
|
-
Appends model + thinking level to each response.`, [
|
|
23579
|
-
[
|
|
23580
|
-
{ label: currentSig === "on" ? "\u2713 On" : "On", data: "model_sig:on", ...currentSig === "on" ? { style: "success" } : {} },
|
|
23581
|
-
{ label: currentSig !== "on" ? "\u2713 Off" : "Off", data: "model_sig:off", ...currentSig !== "on" ? { style: "danger" } : {} }
|
|
23582
|
-
]
|
|
23583
|
-
]);
|
|
23584
|
-
} else {
|
|
23585
|
-
const newSig = currentSig === "on" ? "off" : "on";
|
|
23586
|
-
setModelSignature(chatId, newSig);
|
|
23587
|
-
await channel.sendText(chatId, newSig === "on" ? "Model signature enabled." : "Model signature disabled.", { parseMode: "plain" });
|
|
23588
|
-
}
|
|
24278
|
+
await sendModelSigKeyboard(chatId, channel);
|
|
23589
24279
|
}
|
|
23590
24280
|
async function handleRememberCommand(chatId, commandArgs, msg, channel) {
|
|
23591
24281
|
if (!commandArgs) {
|
|
@@ -23604,15 +24294,29 @@ async function handleRememberCommand(chatId, commandArgs, msg, channel) {
|
|
|
23604
24294
|
}
|
|
23605
24295
|
async function handleForgetCommand(chatId, commandArgs, msg, channel) {
|
|
23606
24296
|
if (!commandArgs) {
|
|
23607
|
-
await
|
|
24297
|
+
await sendForgetPicker(chatId, channel, 1);
|
|
23608
24298
|
return;
|
|
23609
24299
|
}
|
|
23610
|
-
const
|
|
23611
|
-
|
|
23612
|
-
|
|
23613
|
-
count > 0 ? `Forgot ${count} memory(ies) matching "${commandArgs}".` : `No memories found matching "${commandArgs}".`,
|
|
23614
|
-
{ parseMode: "plain" }
|
|
24300
|
+
const keyword = commandArgs.trim();
|
|
24301
|
+
const matching = listMemories().filter(
|
|
24302
|
+
(m) => m.trigger.toLowerCase().includes(keyword.toLowerCase()) || m.content.toLowerCase().includes(keyword.toLowerCase())
|
|
23615
24303
|
);
|
|
24304
|
+
if (matching.length === 0) {
|
|
24305
|
+
await channel.sendText(chatId, `No memories found matching "${keyword}".`, { parseMode: "plain" });
|
|
24306
|
+
return;
|
|
24307
|
+
}
|
|
24308
|
+
const preview = matching.slice(0, 3).map((m) => `\u2022 ${m.trigger}: ${m.content.slice(0, 40)}`).join("\n");
|
|
24309
|
+
const more = matching.length > 3 ? `
|
|
24310
|
+
\u2026and ${matching.length - 3} more` : "";
|
|
24311
|
+
const confirmText = `Delete ${matching.length} memory(ies) matching "${keyword}"?
|
|
24312
|
+
|
|
24313
|
+
${preview}${more}`;
|
|
24314
|
+
await sendOrEditKeyboard(chatId, channel, void 0, confirmText, [
|
|
24315
|
+
[
|
|
24316
|
+
{ label: `\u{1F5D1} Delete ${matching.length}`, data: `forget:keyword:${keyword}`, style: "danger" },
|
|
24317
|
+
{ label: "Cancel", data: "forget:cancel" }
|
|
24318
|
+
]
|
|
24319
|
+
]);
|
|
23616
24320
|
}
|
|
23617
24321
|
async function handleIntentCommand(chatId, commandArgs, msg, channel) {
|
|
23618
24322
|
const testMsg = commandArgs?.trim() || "hey";
|
|
@@ -23835,72 +24539,13 @@ async function handleMenuCommand(chatId, commandArgs, msg, channel) {
|
|
|
23835
24539
|
}
|
|
23836
24540
|
}
|
|
23837
24541
|
async function handlePermissionsCommand(chatId, commandArgs, msg, channel) {
|
|
23838
|
-
|
|
23839
|
-
const currentExecMode = getExecMode(chatId);
|
|
23840
|
-
if (typeof channel.sendKeyboard === "function") {
|
|
23841
|
-
const permButtons = Object.entries(PERM_MODES).map(([id, label2]) => [{
|
|
23842
|
-
label: `${id === currentMode ? "\u2713 " : ""}${label2}`,
|
|
23843
|
-
data: `perms:${id}`,
|
|
23844
|
-
...id === currentMode ? { style: "primary" } : {}
|
|
23845
|
-
}]);
|
|
23846
|
-
const approvalOn = currentExecMode === "approved";
|
|
23847
|
-
permButtons.push([{
|
|
23848
|
-
label: `${approvalOn ? "\u2713 " : ""}\u{1F512} Approve Before Execute: ${approvalOn ? "ON" : "OFF"}`,
|
|
23849
|
-
data: `execmode:${approvalOn ? "yolo" : "approved"}`,
|
|
23850
|
-
...approvalOn ? { style: "primary" } : {}
|
|
23851
|
-
}]);
|
|
23852
|
-
await channel.sendKeyboard(chatId, "Permission & Execution Settings:", permButtons);
|
|
23853
|
-
} else {
|
|
23854
|
-
const lines = ["Permission modes:", ""];
|
|
23855
|
-
for (const [id, label2] of Object.entries(PERM_MODES)) {
|
|
23856
|
-
lines.push(`${id === currentMode ? "\u2713 " : " "}/permissions ${id} \u2014 ${label2}`);
|
|
23857
|
-
}
|
|
23858
|
-
lines.push("");
|
|
23859
|
-
lines.push(`Approve Before Execute: ${currentExecMode === "approved" ? "ON" : "OFF"}`);
|
|
23860
|
-
await channel.sendText(chatId, lines.join("\n"), { parseMode: "plain" });
|
|
23861
|
-
}
|
|
24542
|
+
await sendPermissionsKeyboard(chatId, channel);
|
|
23862
24543
|
}
|
|
23863
24544
|
async function handleVerboseCommand(chatId, commandArgs, msg, channel) {
|
|
23864
|
-
|
|
23865
|
-
const buttons = Object.entries(VERBOSE_LEVELS).map(([id, label2]) => [{
|
|
23866
|
-
label: `${id === currentVerbose ? "\u2713 " : ""}${label2}`,
|
|
23867
|
-
data: `verbose:${id}`,
|
|
23868
|
-
...id === currentVerbose ? { style: "primary" } : {}
|
|
23869
|
-
}]);
|
|
23870
|
-
if (typeof channel.sendKeyboard === "function") {
|
|
23871
|
-
await channel.sendKeyboard(chatId, "Tool visibility level:", buttons);
|
|
23872
|
-
} else {
|
|
23873
|
-
const lines = Object.entries(VERBOSE_LEVELS).map(
|
|
23874
|
-
([id, label2]) => `${id === currentVerbose ? "\u2713 " : " "}${id} \u2014 ${label2}`
|
|
23875
|
-
);
|
|
23876
|
-
await channel.sendText(chatId, ["Tool visibility:", "", ...lines].join("\n"), { parseMode: "plain" });
|
|
23877
|
-
}
|
|
24545
|
+
await sendVerboseKeyboard(chatId, channel);
|
|
23878
24546
|
}
|
|
23879
24547
|
async function handleToolsCommand(chatId, commandArgs, msg, channel) {
|
|
23880
|
-
|
|
23881
|
-
const currentMode = getMode(chatId);
|
|
23882
|
-
if (typeof channel.sendKeyboard === "function") {
|
|
23883
|
-
const toolButtons = [];
|
|
23884
|
-
const toolList = [...ALL_TOOLS];
|
|
23885
|
-
for (let i = 0; i < toolList.length; i += 3) {
|
|
23886
|
-
const row = toolList.slice(i, i + 3).map((t) => ({
|
|
23887
|
-
label: `${toolsMap[t] ? "\u2705" : "\u274C"} ${t}`,
|
|
23888
|
-
data: `tool:toggle:${t}`
|
|
23889
|
-
}));
|
|
23890
|
-
toolButtons.push(row);
|
|
23891
|
-
}
|
|
23892
|
-
toolButtons.push([{ label: "Reset to defaults (all on)", data: "tool:reset", style: "danger" }]);
|
|
23893
|
-
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)." : "";
|
|
23894
|
-
await channel.sendKeyboard(
|
|
23895
|
-
chatId,
|
|
23896
|
-
`Configure allowed tools (mode: ${currentMode})${modeNote}
|
|
23897
|
-
Tap to toggle:`,
|
|
23898
|
-
toolButtons
|
|
23899
|
-
);
|
|
23900
|
-
} else {
|
|
23901
|
-
const lines = ALL_TOOLS.map((t) => `${toolsMap[t] ? "[on] " : "[off]"} ${t}`);
|
|
23902
|
-
await channel.sendText(chatId, ["Allowed tools:", "", ...lines].join("\n"), { parseMode: "plain" });
|
|
23903
|
-
}
|
|
24548
|
+
await sendToolsKeyboard(chatId, channel);
|
|
23904
24549
|
}
|
|
23905
24550
|
async function handleNewchatCommand(chatId, commandArgs, msg, channel) {
|
|
23906
24551
|
stopAllSideQuests(chatId);
|
|
@@ -23940,14 +24585,22 @@ async function handleNewchatCommand(chatId, commandArgs, msg, channel) {
|
|
|
23940
24585
|
const text = `\u2705 New session started. Previous session archived${exchangeCount > 0 ? ` (${exchangeCount} exchanges)` : ""}.
|
|
23941
24586
|
|
|
23942
24587
|
\u{1F9E0} ${backendLabel} \xB7 ${modelLabel}`;
|
|
23943
|
-
|
|
23944
|
-
const kbMsgId = await channel.sendKeyboard(chatId, text, [
|
|
24588
|
+
const kbButtons = [
|
|
23945
24589
|
[
|
|
23946
24590
|
{ label: "Switch Backend", data: "menu:backend", style: "primary" },
|
|
23947
24591
|
{ label: "Switch Model", data: "menu:model", style: "primary" }
|
|
23948
24592
|
],
|
|
23949
24593
|
[{ label: "Undo", data: "newchat:undo" }]
|
|
23950
|
-
]
|
|
24594
|
+
];
|
|
24595
|
+
let kbMsgId;
|
|
24596
|
+
if (ackMsgId && typeof channel.editKeyboard === "function") {
|
|
24597
|
+
const edited = await channel.editKeyboard(chatId, ackMsgId, text, kbButtons);
|
|
24598
|
+
if (edited) kbMsgId = ackMsgId;
|
|
24599
|
+
}
|
|
24600
|
+
if (!kbMsgId) {
|
|
24601
|
+
if (ackMsgId) await channel.editText?.(chatId, ackMsgId, text, "plain");
|
|
24602
|
+
kbMsgId = await channel.sendKeyboard(chatId, text, kbButtons);
|
|
24603
|
+
}
|
|
23951
24604
|
if (kbMsgId) {
|
|
23952
24605
|
const prev = pendingNewchatUndo.get(chatId);
|
|
23953
24606
|
if (prev) clearTimeout(prev.timer);
|
|
@@ -23966,6 +24619,14 @@ async function handleNewchatCommand(chatId, commandArgs, msg, channel) {
|
|
|
23966
24619
|
}
|
|
23967
24620
|
}
|
|
23968
24621
|
}
|
|
24622
|
+
async function handleClearCommand(chatId, _commandArgs, _msg, channel) {
|
|
24623
|
+
stopAllSideQuests(chatId);
|
|
24624
|
+
clearSession(chatId);
|
|
24625
|
+
clearChatPaidSlots(chatId);
|
|
24626
|
+
setSessionStartedAt(chatId);
|
|
24627
|
+
logActivity(getDb(), { chatId, source: "telegram", eventType: "config_changed", summary: "Session cleared (no summary)", detail: { field: "session", action: "clear" } });
|
|
24628
|
+
await channel.sendText(chatId, "\u{1F9FD} Session cleared. No summary saved.", { parseMode: "plain" });
|
|
24629
|
+
}
|
|
23969
24630
|
async function handleSummarizeCommand(chatId, commandArgs, msg, channel) {
|
|
23970
24631
|
if (commandArgs?.toLowerCase() === "all") {
|
|
23971
24632
|
const pendingIds = getLoggedChatIds();
|
|
@@ -24154,25 +24815,13 @@ async function handleStatusCommand(chatId, commandArgs, msg, channel) {
|
|
|
24154
24815
|
}
|
|
24155
24816
|
async function handleBackendCommand2(chatId, commandArgs, msg, channel) {
|
|
24156
24817
|
const requestedBackend = (commandArgs ?? "").trim().toLowerCase();
|
|
24157
|
-
|
|
24818
|
+
const chatBackendIds = getAvailableChatBackendIds();
|
|
24819
|
+
if (requestedBackend && chatBackendIds.includes(requestedBackend)) {
|
|
24158
24820
|
await sendBackendSwitchConfirmation(chatId, requestedBackend, channel);
|
|
24159
24821
|
return;
|
|
24160
24822
|
}
|
|
24161
|
-
const
|
|
24162
|
-
|
|
24163
|
-
if (typeof channel.sendKeyboard === "function") {
|
|
24164
|
-
const buttons = adapters2.map((a) => [{
|
|
24165
|
-
label: `${a.id === currentBackend ? "\u2713 " : ""}${a.displayName}`,
|
|
24166
|
-
data: `backend:${a.id}`,
|
|
24167
|
-
...a.id === currentBackend ? { style: "primary" } : {}
|
|
24168
|
-
}]);
|
|
24169
|
-
await channel.sendKeyboard(chatId, "Select AI backend:", buttons);
|
|
24170
|
-
} else {
|
|
24171
|
-
const lines = adapters2.map(
|
|
24172
|
-
(a) => `${a.id === currentBackend ? "\u2713 " : " "}${a.displayName} (/backend ${a.id})`
|
|
24173
|
-
);
|
|
24174
|
-
await channel.sendText(chatId, ["Available backends:", "", ...lines].join("\n"), { parseMode: "plain" });
|
|
24175
|
-
}
|
|
24823
|
+
const { sendBackendPicker: sendBackendPicker2 } = await Promise.resolve().then(() => (init_ui(), ui_exports));
|
|
24824
|
+
await sendBackendPicker2(chatId, channel);
|
|
24176
24825
|
}
|
|
24177
24826
|
async function handleBackendShortcutCommand(chatId, commandArgs, msg, channel) {
|
|
24178
24827
|
const command = msg.command;
|
|
@@ -24184,82 +24833,10 @@ async function handleBackendShortcutCommand(chatId, commandArgs, msg, channel) {
|
|
|
24184
24833
|
}
|
|
24185
24834
|
}
|
|
24186
24835
|
async function handleModelCommand(chatId, commandArgs, msg, channel) {
|
|
24187
|
-
|
|
24188
|
-
try {
|
|
24189
|
-
adapter = getAdapterForChat(chatId);
|
|
24190
|
-
} catch {
|
|
24191
|
-
await channel.sendText(chatId, "No backend set. Use /backend first.", { parseMode: "plain" });
|
|
24192
|
-
return;
|
|
24193
|
-
}
|
|
24194
|
-
const models = adapter.availableModels;
|
|
24195
|
-
const current = getModel(chatId) ?? adapter.defaultModel;
|
|
24196
|
-
if (typeof channel.sendKeyboard === "function") {
|
|
24197
|
-
const buttons = Object.entries(models).map(([id, info]) => {
|
|
24198
|
-
const tag = info.thinking === "adjustable" ? " \u26A1" : "";
|
|
24199
|
-
return [{
|
|
24200
|
-
label: `${id === current ? "\u2713 " : ""}${info.label}${tag}`,
|
|
24201
|
-
data: `model:${id}`,
|
|
24202
|
-
...id === current ? { style: "primary" } : {}
|
|
24203
|
-
}];
|
|
24204
|
-
});
|
|
24205
|
-
const isAuto = getModel(chatId) === "auto";
|
|
24206
|
-
buttons.unshift([{
|
|
24207
|
-
label: `${isAuto ? "\u2713 " : ""}\u{1F916} Auto (smart routing)`,
|
|
24208
|
-
data: "model:auto",
|
|
24209
|
-
...isAuto ? { style: "primary" } : {}
|
|
24210
|
-
}]);
|
|
24211
|
-
await channel.sendKeyboard(chatId, `Models for ${adapter.displayName}:`, buttons);
|
|
24212
|
-
} else {
|
|
24213
|
-
const lines = Object.entries(models).map(
|
|
24214
|
-
([id, info]) => `${id === current ? "\u2713 " : " "}${id} \u2014 ${info.label}`
|
|
24215
|
-
);
|
|
24216
|
-
await channel.sendText(chatId, [`Models (${adapter.displayName}):`, "", ...lines].join("\n"), { parseMode: "plain" });
|
|
24217
|
-
}
|
|
24836
|
+
await sendModelKeyboard(chatId, channel);
|
|
24218
24837
|
}
|
|
24219
24838
|
async function handleThinkingCommand(chatId, commandArgs, msg, channel) {
|
|
24220
|
-
|
|
24221
|
-
try {
|
|
24222
|
-
adapter = getAdapterForChat(chatId);
|
|
24223
|
-
} catch {
|
|
24224
|
-
await channel.sendText(chatId, "No backend set. Use /backend first.", { parseMode: "plain" });
|
|
24225
|
-
return;
|
|
24226
|
-
}
|
|
24227
|
-
const currentModel = getModel(chatId) ?? adapter.defaultModel;
|
|
24228
|
-
const modelInfo = adapter.availableModels[currentModel];
|
|
24229
|
-
const currentLevel = getThinkingLevel(chatId) || "auto";
|
|
24230
|
-
if (!modelInfo || modelInfo.thinking !== "adjustable" || !modelInfo.thinkingLevels) {
|
|
24231
|
-
await channel.sendText(chatId, `Current model (${shortModelName(currentModel)}) doesn't support adjustable thinking.
|
|
24232
|
-
Use /model to pick a model with \u26A1 thinking support.`, { parseMode: "plain" });
|
|
24233
|
-
return;
|
|
24234
|
-
}
|
|
24235
|
-
if (typeof channel.sendKeyboard === "function") {
|
|
24236
|
-
const showThinkingUi = getShowThinkingUi(chatId);
|
|
24237
|
-
const buttons = modelInfo.thinkingLevels.map((level) => [{
|
|
24238
|
-
label: `${level === currentLevel ? "\u2713 " : ""}${level === "auto" ? "Auto" : capitalize(level)}`,
|
|
24239
|
-
data: `thinking:${level}`,
|
|
24240
|
-
...level === currentLevel ? { style: "primary" } : {}
|
|
24241
|
-
}]);
|
|
24242
|
-
buttons.push([{
|
|
24243
|
-
label: `${showThinkingUi ? "\u2713 " : ""}\u{1F4AD} Show Thinking`,
|
|
24244
|
-
data: "thinking_show_ui:toggle",
|
|
24245
|
-
...showThinkingUi ? { style: "primary" } : {}
|
|
24246
|
-
}]);
|
|
24247
|
-
await channel.sendKeyboard(
|
|
24248
|
-
chatId,
|
|
24249
|
-
`\u{1F4AD} Thinking Level \u2014 ${shortModelName(currentModel)}
|
|
24250
|
-
Current: ${capitalize(currentLevel)}
|
|
24251
|
-
Show thinking tokens: ${showThinkingUi ? "On" : "Off"}${adapter.id !== "claude" ? `
|
|
24252
|
-
|
|
24253
|
-
\u26A0\uFE0F ${adapter.displayName} CLI doesn't stream thinking tokens` : ""}`,
|
|
24254
|
-
buttons
|
|
24255
|
-
);
|
|
24256
|
-
} else {
|
|
24257
|
-
const showThinkingUi = getShowThinkingUi(chatId);
|
|
24258
|
-
await channel.sendText(chatId, `Thinking: ${capitalize(currentLevel)}
|
|
24259
|
-
Levels: ${modelInfo.thinkingLevels.join(", ")}
|
|
24260
|
-
Show thinking tokens: ${showThinkingUi ? "On" : "Off"}
|
|
24261
|
-
Set via callback (keyboard required).`, { parseMode: "plain" });
|
|
24262
|
-
}
|
|
24839
|
+
await sendThinkingKeyboard(chatId, channel);
|
|
24263
24840
|
}
|
|
24264
24841
|
async function handleUsageCommand(chatId, commandArgs, msg, channel, command) {
|
|
24265
24842
|
if (commandArgs && command === "limits") {
|
|
@@ -25359,6 +25936,9 @@ async function handleCommand(msg, channel) {
|
|
|
25359
25936
|
case "newchat":
|
|
25360
25937
|
await handleNewchatCommand(chatId, commandArgs, msg, channel);
|
|
25361
25938
|
break;
|
|
25939
|
+
case "clear":
|
|
25940
|
+
await handleClearCommand(chatId, commandArgs, msg, channel);
|
|
25941
|
+
break;
|
|
25362
25942
|
case "summarize":
|
|
25363
25943
|
await handleSummarizeCommand(chatId, commandArgs, msg, channel);
|
|
25364
25944
|
break;
|
|
@@ -25565,25 +26145,45 @@ async function handleCallback(chatId, data, channel, messageId) {
|
|
|
25565
26145
|
await channel.sendText(chatId, text, { parseMode: "plain" });
|
|
25566
26146
|
}
|
|
25567
26147
|
};
|
|
25568
|
-
const MENU_COMMANDS = {
|
|
25569
|
-
newchat: "newchat",
|
|
25570
|
-
status: "status",
|
|
25571
|
-
backend: "backend",
|
|
25572
|
-
model: "model",
|
|
25573
|
-
jobs: "jobs",
|
|
25574
|
-
memory: "memory",
|
|
25575
|
-
history: "history",
|
|
25576
|
-
help: "help",
|
|
25577
|
-
permissions: "permissions",
|
|
25578
|
-
style: "response_style",
|
|
25579
|
-
health: "health"
|
|
25580
|
-
};
|
|
25581
26148
|
if (data.startsWith("menu:")) {
|
|
25582
26149
|
const action = data.slice(5);
|
|
25583
|
-
|
|
25584
|
-
|
|
25585
|
-
|
|
25586
|
-
|
|
26150
|
+
switch (action) {
|
|
26151
|
+
case "backend":
|
|
26152
|
+
await sendBackendPicker(chatId, channel, messageId);
|
|
26153
|
+
break;
|
|
26154
|
+
case "model":
|
|
26155
|
+
await sendModelKeyboard(chatId, channel, messageId);
|
|
26156
|
+
break;
|
|
26157
|
+
case "memory":
|
|
26158
|
+
await sendMemoryPage(chatId, channel, 1, messageId);
|
|
26159
|
+
break;
|
|
26160
|
+
case "history":
|
|
26161
|
+
await sendHistoryView(chatId, channel, {}, messageId);
|
|
26162
|
+
break;
|
|
26163
|
+
case "help":
|
|
26164
|
+
await sendHelpCategories(chatId, channel, messageId);
|
|
26165
|
+
break;
|
|
26166
|
+
case "permissions":
|
|
26167
|
+
await sendPermissionsKeyboard(chatId, channel, messageId);
|
|
26168
|
+
break;
|
|
26169
|
+
case "style":
|
|
26170
|
+
await sendResponseStyleKeyboard(chatId, channel, messageId);
|
|
26171
|
+
break;
|
|
26172
|
+
case "jobs":
|
|
26173
|
+
await sendJobsBoard(chatId, channel, 1, messageId);
|
|
26174
|
+
break;
|
|
26175
|
+
default: {
|
|
26176
|
+
const MENU_COMMANDS = {
|
|
26177
|
+
newchat: "newchat",
|
|
26178
|
+
status: "status",
|
|
26179
|
+
health: "health"
|
|
26180
|
+
};
|
|
26181
|
+
const cmd = MENU_COMMANDS[action];
|
|
26182
|
+
if (cmd) {
|
|
26183
|
+
const synth = { chatId, messageId: "", text: "", senderName: "User", type: "command", source: "telegram", command: cmd, commandArgs: "" };
|
|
26184
|
+
await handleCommand(synth, channel);
|
|
26185
|
+
}
|
|
26186
|
+
}
|
|
25587
26187
|
}
|
|
25588
26188
|
return;
|
|
25589
26189
|
}
|
|
@@ -25592,32 +26192,79 @@ async function handleCallback(chatId, data, channel, messageId) {
|
|
|
25592
26192
|
if (!getAllBackendIds().includes(chosen)) return;
|
|
25593
26193
|
const previous = getBackend(chatId);
|
|
25594
26194
|
if (chosen === previous) {
|
|
25595
|
-
|
|
25596
|
-
await channel.sendText(chatId, `Already using ${adapter.displayName}.`, { parseMode: "plain" });
|
|
26195
|
+
await sendBackendConfigPanel(chatId, chosen, channel, messageId, false);
|
|
25597
26196
|
return;
|
|
25598
26197
|
}
|
|
25599
|
-
await
|
|
26198
|
+
await doBackendSwitch(chatId, chosen, channel, { messageId });
|
|
26199
|
+
} else if (data.startsWith("bconf:")) {
|
|
26200
|
+
const rest = data.slice(6);
|
|
26201
|
+
if (rest === "back") {
|
|
26202
|
+
await sendBackendPicker(chatId, channel, messageId);
|
|
26203
|
+
} else if (rest.startsWith("panel:")) {
|
|
26204
|
+
const backendId = rest.slice(6);
|
|
26205
|
+
await sendBackendConfigPanel(chatId, backendId, channel, messageId, false);
|
|
26206
|
+
} else if (rest.startsWith("model:")) {
|
|
26207
|
+
const backendId = rest.slice(6);
|
|
26208
|
+
await sendBackendModelPicker(chatId, backendId, channel, messageId);
|
|
26209
|
+
} else if (rest.startsWith("thinking:")) {
|
|
26210
|
+
const backendId = rest.slice(9);
|
|
26211
|
+
await sendBackendThinkingPicker(chatId, backendId, channel, messageId);
|
|
26212
|
+
} else if (rest.startsWith("account:")) {
|
|
26213
|
+
const backendId = rest.slice(8);
|
|
26214
|
+
await sendBackendAccountPicker(chatId, backendId, channel, messageId);
|
|
26215
|
+
} else if (rest.startsWith("setmodel:")) {
|
|
26216
|
+
const parts = rest.slice(9).split(":");
|
|
26217
|
+
const backendId = parts[0];
|
|
26218
|
+
const modelId = parts.slice(1).join(":");
|
|
26219
|
+
setModel(chatId, modelId);
|
|
26220
|
+
await sendBackendConfigPanel(chatId, backendId, channel, messageId, false);
|
|
26221
|
+
} else if (rest.startsWith("setthinking:")) {
|
|
26222
|
+
const parts = rest.slice(12).split(":");
|
|
26223
|
+
const backendId = parts[0];
|
|
26224
|
+
const level = parts[1];
|
|
26225
|
+
setThinkingLevel(chatId, level);
|
|
26226
|
+
await sendBackendConfigPanel(chatId, backendId, channel, messageId, false);
|
|
26227
|
+
} else if (rest.startsWith("setaccount:")) {
|
|
26228
|
+
const parts = rest.slice(11).split(":");
|
|
26229
|
+
const backendId = parts[0];
|
|
26230
|
+
const slotIdStr = parts[1];
|
|
26231
|
+
if (slotIdStr === "auto") {
|
|
26232
|
+
if (backendId === "gemini") {
|
|
26233
|
+
clearChatGeminiSlot(chatId);
|
|
26234
|
+
} else {
|
|
26235
|
+
clearChatBackendSlot(chatId, backendId);
|
|
26236
|
+
}
|
|
26237
|
+
} else {
|
|
26238
|
+
const slotId = parseInt(slotIdStr, 10);
|
|
26239
|
+
if (backendId === "gemini") {
|
|
26240
|
+
pinChatGeminiSlot(chatId, slotId);
|
|
26241
|
+
} else {
|
|
26242
|
+
pinChatBackendSlot(chatId, backendId, slotId);
|
|
26243
|
+
}
|
|
26244
|
+
}
|
|
26245
|
+
await sendBackendConfigPanel(chatId, backendId, channel, messageId, false);
|
|
26246
|
+
}
|
|
25600
26247
|
} else if (data.startsWith("backend_confirm_clear:")) {
|
|
25601
26248
|
const target = data.slice("backend_confirm_clear:".length);
|
|
25602
26249
|
if (!getAllBackendIds().includes(target)) return;
|
|
25603
26250
|
clearMessageLog(chatId);
|
|
25604
|
-
await doBackendSwitch(chatId, target, channel);
|
|
26251
|
+
await doBackendSwitch(chatId, target, channel, { skipContext: true, messageId });
|
|
25605
26252
|
} else if (data.startsWith("backend_confirm:")) {
|
|
25606
26253
|
const chosen = data.slice(16);
|
|
25607
26254
|
if (!getAllBackendIds().includes(chosen)) return;
|
|
25608
|
-
await doBackendSwitch(chatId, chosen, channel);
|
|
26255
|
+
await doBackendSwitch(chatId, chosen, channel, { messageId });
|
|
25609
26256
|
} else if (data.startsWith("backend_cancel:") || data === "backend_cancel") {
|
|
25610
|
-
await
|
|
26257
|
+
await sendBackendPicker(chatId, channel, messageId);
|
|
25611
26258
|
} else if (data.startsWith("backend_dismiss:")) {
|
|
25612
26259
|
const backendId = data.slice(16);
|
|
25613
|
-
|
|
25614
|
-
await channel.sendText(chatId, `Using ${adapter.displayName} with default model. Ready!`, { parseMode: "plain" });
|
|
26260
|
+
await sendBackendConfigPanel(chatId, backendId, channel, messageId, false);
|
|
25615
26261
|
} else if (data.startsWith("model:")) {
|
|
25616
26262
|
const chosen = data.slice(6);
|
|
25617
26263
|
if (chosen === "auto") {
|
|
25618
26264
|
setModel(chatId, "auto");
|
|
25619
26265
|
clearThinkingLevel(chatId);
|
|
25620
|
-
|
|
26266
|
+
logActivity(getDb(), { chatId, source: "telegram", eventType: "config_changed", summary: "Smart routing enabled", detail: { field: "model", value: "auto" } });
|
|
26267
|
+
await sendModelKeyboard(chatId, channel, messageId);
|
|
25621
26268
|
return;
|
|
25622
26269
|
}
|
|
25623
26270
|
let adapter;
|
|
@@ -25633,45 +26280,19 @@ async function handleCallback(chatId, data, channel, messageId) {
|
|
|
25633
26280
|
clearThinkingLevel(chatId);
|
|
25634
26281
|
logActivity(getDb(), { chatId, source: "telegram", eventType: "config_changed", summary: `Model switched to ${modelInfo.label}`, detail: { field: "model", value: chosen } });
|
|
25635
26282
|
if (modelInfo.thinking === "adjustable" && modelInfo.thinkingLevels) {
|
|
25636
|
-
|
|
25637
|
-
label: level === "auto" ? "Auto (default)" : level.replace("_", " ").replace(/\b\w/g, (c) => c.toUpperCase()),
|
|
25638
|
-
data: `thinking:${level}`
|
|
25639
|
-
}]);
|
|
25640
|
-
if (typeof channel.sendKeyboard === "function") {
|
|
25641
|
-
await channel.sendKeyboard(
|
|
25642
|
-
chatId,
|
|
25643
|
-
`Model set to ${modelInfo.label}. Session continues.
|
|
25644
|
-
|
|
25645
|
-
Select thinking/effort level:`,
|
|
25646
|
-
thinkingButtons
|
|
25647
|
-
);
|
|
25648
|
-
} else {
|
|
25649
|
-
await channel.sendText(chatId, `Model set to ${modelInfo.label}. Session continues.`, { parseMode: "plain" });
|
|
25650
|
-
}
|
|
26283
|
+
await sendThinkingKeyboard(chatId, channel, messageId, chosen);
|
|
25651
26284
|
} else {
|
|
25652
|
-
await
|
|
26285
|
+
await sendModelKeyboard(chatId, channel, messageId);
|
|
25653
26286
|
}
|
|
25654
26287
|
} else if (data.startsWith("thinking:")) {
|
|
25655
26288
|
const level = data.slice(9);
|
|
25656
26289
|
setThinkingLevel(chatId, level);
|
|
25657
26290
|
logActivity(getDb(), { chatId, source: "telegram", eventType: "config_changed", summary: `Thinking level set to ${level}`, detail: { field: "thinking", value: level } });
|
|
25658
|
-
|
|
25659
|
-
await channel.sendText(chatId, `Thinking level set to: ${label2}`, { parseMode: "plain" });
|
|
26291
|
+
await sendThinkingKeyboard(chatId, channel, messageId);
|
|
25660
26292
|
} else if (data === "thinking_show_ui:toggle") {
|
|
25661
26293
|
const newState = toggleShowThinkingUi(chatId);
|
|
25662
26294
|
logActivity(getDb(), { chatId, source: "telegram", eventType: "config_changed", summary: `Show thinking UI ${newState ? "enabled" : "disabled"}`, detail: { field: "show_thinking_ui", value: String(newState) } });
|
|
25663
|
-
|
|
25664
|
-
if (newState) {
|
|
25665
|
-
let backendId;
|
|
25666
|
-
try {
|
|
25667
|
-
backendId = getAdapterForChat(chatId).id;
|
|
25668
|
-
} catch {
|
|
25669
|
-
}
|
|
25670
|
-
msg = backendId && backendId !== "claude" ? `\u{1F4AD} Thinking tokens enabled \u2014 but ${capitalize(backendId)} CLI doesn't stream them.` : "\u{1F4AD} Thinking tokens will now stream live in the status message.";
|
|
25671
|
-
} else {
|
|
25672
|
-
msg = "\u{1F4AD} Thinking tokens hidden. Toggle on again via /thinking.";
|
|
25673
|
-
}
|
|
25674
|
-
await channel.sendText(chatId, msg, { parseMode: "plain" });
|
|
26295
|
+
await sendThinkingKeyboard(chatId, channel, messageId);
|
|
25675
26296
|
} else if (data.startsWith("debug_log:")) {
|
|
25676
26297
|
const value = data.slice(10) === "on";
|
|
25677
26298
|
const { setSessionLogEnabled: setSessionLogEnabled2 } = await Promise.resolve().then(() => (init_chat_settings(), chat_settings_exports));
|
|
@@ -25698,19 +26319,9 @@ ${value ? "Full tool inputs/results will be saved to ~/.cc-claw/logs/sessions/"
|
|
|
25698
26319
|
let chosen = data.slice(6);
|
|
25699
26320
|
if (chosen === "readonly") chosen = "plan";
|
|
25700
26321
|
if (!PERM_MODES[chosen]) return;
|
|
25701
|
-
const previous = getMode(chatId);
|
|
25702
|
-
if (chosen === previous) {
|
|
25703
|
-
await channel.sendText(chatId, `Already in ${chosen} mode.`, { parseMode: "plain" });
|
|
25704
|
-
return;
|
|
25705
|
-
}
|
|
25706
26322
|
setMode(chatId, chosen);
|
|
25707
26323
|
logActivity(getDb(), { chatId, source: "telegram", eventType: "config_changed", summary: `Permission mode set to ${chosen}`, detail: { field: "permissions", value: chosen } });
|
|
25708
|
-
await channel
|
|
25709
|
-
chatId,
|
|
25710
|
-
`Permission mode: ${chosen}
|
|
25711
|
-
${PERM_MODES[chosen]}`,
|
|
25712
|
-
{ parseMode: "plain" }
|
|
25713
|
-
);
|
|
26324
|
+
await sendPermissionsKeyboard(chatId, channel, messageId);
|
|
25714
26325
|
} else if (data.startsWith("perm:escalate:")) {
|
|
25715
26326
|
const targetMode = data.slice(14);
|
|
25716
26327
|
if (!PERM_MODES[targetMode]) return;
|
|
@@ -25730,33 +26341,20 @@ ${PERM_MODES[chosen]}`,
|
|
|
25730
26341
|
const chosen = data.slice(8);
|
|
25731
26342
|
if (!VERBOSE_LEVELS[chosen]) return;
|
|
25732
26343
|
setVerboseLevel(chatId, chosen);
|
|
25733
|
-
await
|
|
26344
|
+
await sendVerboseKeyboard(chatId, channel, messageId);
|
|
25734
26345
|
} else if (data.startsWith("tool:toggle:")) {
|
|
25735
26346
|
const toolName = data.slice(12);
|
|
25736
26347
|
if (!ALL_TOOLS.includes(toolName)) return;
|
|
25737
|
-
|
|
25738
|
-
await channel
|
|
25739
|
-
chatId,
|
|
25740
|
-
`${newState ? "\u2705" : "\u274C"} ${toolName} ${newState ? "enabled" : "disabled"}`,
|
|
25741
|
-
{ parseMode: "plain" }
|
|
25742
|
-
);
|
|
26348
|
+
toggleTool(chatId, toolName);
|
|
26349
|
+
await sendToolsKeyboard(chatId, channel, messageId);
|
|
25743
26350
|
} else if (data === "tool:reset") {
|
|
25744
26351
|
resetTools(chatId);
|
|
25745
|
-
await
|
|
26352
|
+
await sendToolsKeyboard(chatId, channel, messageId);
|
|
25746
26353
|
} else if (data.startsWith("style:")) {
|
|
25747
26354
|
const selectedStyle = data.split(":")[1];
|
|
25748
26355
|
if (selectedStyle === "concise" || selectedStyle === "normal" || selectedStyle === "detailed") {
|
|
25749
26356
|
setResponseStyle(chatId, selectedStyle);
|
|
25750
|
-
|
|
25751
|
-
await channel.sendKeyboard(chatId, "\u{1F5E3}\uFE0F AI Response Style:", [
|
|
25752
|
-
[
|
|
25753
|
-
{ label: `${selectedStyle === "concise" ? "\u2713 " : ""}Concise`, data: "style:concise", ...selectedStyle === "concise" ? { style: "primary" } : {} },
|
|
25754
|
-
{ label: `${selectedStyle === "normal" ? "\u2713 " : ""}Normal`, data: "style:normal", ...selectedStyle === "normal" ? { style: "primary" } : {} },
|
|
25755
|
-
{ label: `${selectedStyle === "detailed" ? "\u2713 " : ""}Detailed`, data: "style:detailed", ...selectedStyle === "detailed" ? { style: "primary" } : {} }
|
|
25756
|
-
]
|
|
25757
|
-
]);
|
|
25758
|
-
}
|
|
25759
|
-
await channel.sendText(chatId, `Response style set to: ${selectedStyle}`, { parseMode: "plain" });
|
|
26357
|
+
await sendResponseStyleKeyboard(chatId, channel, messageId);
|
|
25760
26358
|
}
|
|
25761
26359
|
} else if (data.startsWith("agentmode:")) {
|
|
25762
26360
|
const mode = data.split(":")[1];
|
|
@@ -25825,25 +26423,20 @@ ${plan.originalMessage}`;
|
|
|
25825
26423
|
const mode = data.split(":")[1];
|
|
25826
26424
|
if (mode === "approved" || mode === "yolo") {
|
|
25827
26425
|
setExecMode(chatId, mode);
|
|
25828
|
-
|
|
25829
|
-
await channel.sendText(chatId, msg, { parseMode: "html" });
|
|
26426
|
+
await sendPermissionsKeyboard(chatId, channel, messageId);
|
|
25830
26427
|
}
|
|
25831
26428
|
return;
|
|
25832
26429
|
} else if (data.startsWith("model_sig:")) {
|
|
25833
26430
|
const value = data.slice(10);
|
|
25834
26431
|
setModelSignature(chatId, value);
|
|
25835
|
-
await channel
|
|
25836
|
-
chatId,
|
|
25837
|
-
value === "on" ? "\u{1F9E0} Model signature enabled. Each response will show the active model and thinking level." : "Model signature disabled.",
|
|
25838
|
-
{ parseMode: "plain" }
|
|
25839
|
-
);
|
|
26432
|
+
await sendModelSigKeyboard(chatId, channel, messageId);
|
|
25840
26433
|
} else if (data.startsWith("voice:")) {
|
|
25841
26434
|
const action = data.slice(6);
|
|
25842
26435
|
if (action === "on" || action === "off") {
|
|
25843
26436
|
const current = isVoiceEnabled(chatId);
|
|
25844
26437
|
const desired = action === "on";
|
|
25845
26438
|
if (current !== desired) toggleVoice(chatId);
|
|
25846
|
-
await
|
|
26439
|
+
await sendVoiceConfigKeyboard(chatId, channel, messageId);
|
|
25847
26440
|
}
|
|
25848
26441
|
} else if (data.startsWith("vcfg:stt-model:")) {
|
|
25849
26442
|
const model2 = data.slice(15);
|
|
@@ -25855,64 +26448,81 @@ ${plan.originalMessage}`;
|
|
|
25855
26448
|
setSttModel(chatId, model2);
|
|
25856
26449
|
const info = LOCAL_WHISPER_MODELS[model2];
|
|
25857
26450
|
const downloaded = isWhisperModelDownloaded(model2);
|
|
25858
|
-
|
|
25859
|
-
|
|
25860
|
-
|
|
25861
|
-
|
|
25862
|
-
|
|
25863
|
-
} else if (data.startsWith("vcfg:stt:")) {
|
|
25864
|
-
const provider = data.slice(9);
|
|
25865
|
-
if (provider === "local-whisper") {
|
|
25866
|
-
if (!isWhisperCliAvailable()) {
|
|
25867
|
-
await channel.sendText(
|
|
26451
|
+
if (downloaded) {
|
|
26452
|
+
await sendVoiceConfigKeyboard(chatId, channel, messageId);
|
|
26453
|
+
} else {
|
|
26454
|
+
if (messageId && channel.editKeyboard) {
|
|
26455
|
+
await channel.editKeyboard(
|
|
25868
26456
|
chatId,
|
|
25869
|
-
|
|
25870
|
-
{
|
|
26457
|
+
messageId,
|
|
26458
|
+
`\u{1F399}\uFE0F Voice Settings
|
|
26459
|
+
|
|
26460
|
+
\u2B07\uFE0F Downloading ${model2} (${info.size})\u2026
|
|
26461
|
+
This may take a moment.`,
|
|
26462
|
+
[]
|
|
25871
26463
|
);
|
|
25872
|
-
|
|
26464
|
+
} else {
|
|
26465
|
+
await channel.sendText(chatId, `\u2B07\uFE0F Downloading ${model2} (${info.size})...`, { parseMode: "plain" });
|
|
26466
|
+
}
|
|
26467
|
+
try {
|
|
26468
|
+
await downloadWhisperModel(model2, async (progressMsg) => {
|
|
26469
|
+
if (messageId && channel.editKeyboard) {
|
|
26470
|
+
await channel.editKeyboard(
|
|
26471
|
+
chatId,
|
|
26472
|
+
messageId,
|
|
26473
|
+
`\u{1F399}\uFE0F Voice Settings
|
|
26474
|
+
|
|
26475
|
+
${progressMsg}`,
|
|
26476
|
+
[]
|
|
26477
|
+
);
|
|
26478
|
+
}
|
|
26479
|
+
});
|
|
26480
|
+
await sendVoiceConfigKeyboard(chatId, channel, messageId);
|
|
26481
|
+
} catch (err) {
|
|
26482
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
26483
|
+
const failMsg = `\u{1F399}\uFE0F Voice Settings
|
|
26484
|
+
|
|
26485
|
+
\u274C Download failed: ${errMsg}
|
|
26486
|
+
|
|
26487
|
+
Tap the model again to retry.`;
|
|
26488
|
+
if (messageId && channel.editKeyboard) {
|
|
26489
|
+
await sendVoiceConfigKeyboard(chatId, channel, messageId);
|
|
26490
|
+
} else {
|
|
26491
|
+
await channel.sendText(chatId, failMsg, { parseMode: "plain" });
|
|
26492
|
+
}
|
|
25873
26493
|
}
|
|
25874
26494
|
}
|
|
25875
|
-
|
|
25876
|
-
|
|
26495
|
+
} else if (data.startsWith("vcfg:stt:")) {
|
|
26496
|
+
const provider = data.slice(9);
|
|
26497
|
+
if (provider === "local-whisper" && !isFfmpegAvailable()) {
|
|
26498
|
+
await sendOrEditKeyboard(
|
|
25877
26499
|
chatId,
|
|
25878
|
-
|
|
25879
|
-
|
|
26500
|
+
channel,
|
|
26501
|
+
messageId,
|
|
26502
|
+
"\u{1F399}\uFE0F Voice Settings\n\n\u26A0\uFE0F Local Whisper requires ffmpeg to convert audio.\n\nInstall: brew install ffmpeg\n\nThen tap Local Whisper again.",
|
|
26503
|
+
[[{ label: "\u2190 Back", data: "vcfg:stt:groq" }]]
|
|
25880
26504
|
);
|
|
26505
|
+
return;
|
|
26506
|
+
}
|
|
26507
|
+
if (provider === "groq" && !process.env.GROQ_API_KEY) {
|
|
25881
26508
|
}
|
|
25882
26509
|
setSttProvider(chatId, provider);
|
|
25883
|
-
|
|
25884
|
-
await channel.sendText(chatId, `\u2705 Transcription provider set to: ${label2}`, { parseMode: "plain" });
|
|
25885
|
-
await sendVoiceConfigKeyboard(chatId, channel);
|
|
26510
|
+
await sendVoiceConfigKeyboard(chatId, channel, messageId);
|
|
25886
26511
|
} else if (data.startsWith("vcfg:")) {
|
|
25887
26512
|
const parts = data.slice(5).split(":");
|
|
25888
26513
|
const action = parts[0];
|
|
25889
26514
|
if (action === "p") {
|
|
25890
26515
|
const provider = parts[1];
|
|
25891
26516
|
if (provider === "elevenlabs" || provider === "grok" || provider === "macos") {
|
|
25892
|
-
if (provider === "grok" && !process.env.XAI_API_KEY) {
|
|
25893
|
-
await channel.sendText(
|
|
25894
|
-
chatId,
|
|
25895
|
-
"\u26A0\uFE0F Grok requires `XAI_API_KEY` to be set.\n\nAdd it to your config:\n```\necho 'XAI_API_KEY=your-key-here' >> ~/.cc-claw/.env\ncc-claw service restart\n```\n\nGet a key at: https://console.x.ai/team/default/api-keys\n\nSetting Grok as your provider \u2014 it will activate once the key is added.",
|
|
25896
|
-
{ parseMode: "markdown" }
|
|
25897
|
-
);
|
|
25898
|
-
}
|
|
25899
|
-
if (provider === "elevenlabs" && !process.env.ELEVENLABS_API_KEY) {
|
|
25900
|
-
await channel.sendText(
|
|
25901
|
-
chatId,
|
|
25902
|
-
"\u26A0\uFE0F ElevenLabs requires `ELEVENLABS_API_KEY` to be set.\n\nAdd it to your config:\n```\necho 'ELEVENLABS_API_KEY=your-key-here' >> ~/.cc-claw/.env\ncc-claw service restart\n```\n\nGet a key at: https://elevenlabs.io/api\n\nSetting ElevenLabs as your provider \u2014 it will activate once the key is added.",
|
|
25903
|
-
{ parseMode: "markdown" }
|
|
25904
|
-
);
|
|
25905
|
-
}
|
|
25906
26517
|
const defaultVoice = provider === "grok" ? "eve" : provider === "macos" ? "Samantha" : "21m00Tcm4TlvDq8ikWAM";
|
|
25907
26518
|
setVoiceProvider(chatId, provider, defaultVoice);
|
|
25908
|
-
await sendVoiceConfigKeyboard(chatId, channel);
|
|
26519
|
+
await sendVoiceConfigKeyboard(chatId, channel, messageId);
|
|
25909
26520
|
}
|
|
25910
26521
|
} else if (action === "v") {
|
|
25911
26522
|
const voiceId = parts.slice(1).join(":");
|
|
25912
26523
|
const config2 = getVoiceConfig(chatId);
|
|
25913
26524
|
setVoiceProvider(chatId, config2.provider, voiceId);
|
|
25914
|
-
|
|
25915
|
-
await channel.sendText(chatId, `\u2705 Voice set to: ${voiceName}`, { parseMode: "plain" });
|
|
26525
|
+
await sendVoiceConfigKeyboard(chatId, channel, messageId);
|
|
25916
26526
|
}
|
|
25917
26527
|
} else if (data.startsWith("profile:")) {
|
|
25918
26528
|
await handleProfileCallback(chatId, data, channel);
|
|
@@ -25970,14 +26580,14 @@ ${plan.originalMessage}`;
|
|
|
25970
26580
|
try {
|
|
25971
26581
|
const { askAgent: askAgent3 } = await Promise.resolve().then(() => (init_agent(), agent_exports));
|
|
25972
26582
|
const { BACKEND: BACKEND2 } = await Promise.resolve().then(() => (init_types(), types_exports));
|
|
25973
|
-
const { pinChatGeminiSlot:
|
|
26583
|
+
const { pinChatGeminiSlot: pinChatGeminiSlot5, pinChatBackendSlot: pinChatBackendSlot4 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
|
|
25974
26584
|
const testChatId = `cron:test:${id}:${Date.now()}`;
|
|
25975
26585
|
const backend2 = testJob.backend ?? "claude";
|
|
25976
26586
|
if (testJob.credentialSlotId) {
|
|
25977
26587
|
if (backend2 === BACKEND2.GEMINI) {
|
|
25978
|
-
|
|
26588
|
+
pinChatGeminiSlot5(testChatId, testJob.credentialSlotId);
|
|
25979
26589
|
} else {
|
|
25980
|
-
|
|
26590
|
+
pinChatBackendSlot4(testChatId, backend2, testJob.credentialSlotId);
|
|
25981
26591
|
}
|
|
25982
26592
|
}
|
|
25983
26593
|
const t0 = Date.now();
|
|
@@ -26305,74 +26915,73 @@ Result: ${task.result.slice(0, 500)}` : ""
|
|
|
26305
26915
|
} else if (data.startsWith("grotation:")) {
|
|
26306
26916
|
const mode = data.split(":")[1];
|
|
26307
26917
|
await handleRotationModeChange(chatId, channel, "gemini", mode);
|
|
26918
|
+
const slots = getGeminiSlots();
|
|
26919
|
+
const rows = buildAccountSlotKeyboard(slots, getChatGeminiSlotId(chatId), getGeminiRotationMode(), "gslot:", "grotation:");
|
|
26920
|
+
await sendOrEditKeyboard(chatId, channel, messageId, "\u{1F511} Gemini Accounts & Rotation:", rows);
|
|
26308
26921
|
return;
|
|
26309
26922
|
} else if (data.startsWith("brotation:")) {
|
|
26310
26923
|
const parts = data.split(":");
|
|
26311
26924
|
const backend2 = parts[1];
|
|
26312
26925
|
const mode = parts[2];
|
|
26313
26926
|
await handleRotationModeChange(chatId, channel, backend2, mode);
|
|
26927
|
+
const { getBackendRotationMode: getBackendRotationMode2 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
|
|
26928
|
+
const slots = getBackendSlots(backend2);
|
|
26929
|
+
const rows = buildAccountSlotKeyboard(slots, getChatBackendSlotId(chatId, backend2), getBackendRotationMode2(backend2), `bslot:${backend2}:`, `brotation:${backend2}:`);
|
|
26930
|
+
const displayName = backend2 === BACKEND.CLAUDE ? "Claude" : "Codex";
|
|
26931
|
+
await sendOrEditKeyboard(chatId, channel, messageId, `\u{1F511} ${displayName} Accounts & Rotation:`, rows);
|
|
26314
26932
|
return;
|
|
26315
26933
|
} else if (data.startsWith("gslot:")) {
|
|
26316
26934
|
const val = data.split(":")[1];
|
|
26317
26935
|
if (val === "auto") {
|
|
26318
26936
|
clearChatGeminiSlot(chatId);
|
|
26319
26937
|
clearSession(chatId);
|
|
26320
|
-
await channel.sendText(chatId, "Gemini slot set to <b>\u{1F504} auto rotation</b>.", { parseMode: "html" });
|
|
26321
26938
|
} else {
|
|
26322
26939
|
const slotId = parseInt(val, 10);
|
|
26323
|
-
const
|
|
26324
|
-
const slot = slots.find((s) => s.id === slotId);
|
|
26940
|
+
const slot = getGeminiSlots().find((s) => s.id === slotId);
|
|
26325
26941
|
if (slot) {
|
|
26326
26942
|
pinChatGeminiSlot(chatId, slotId);
|
|
26327
26943
|
clearSession(chatId);
|
|
26328
|
-
const label2 = slot.label || `slot-${slot.id}`;
|
|
26329
|
-
const icon = slot.slotType === "oauth" ? "\u{1F468}\u{1F3FD}\u200D\u{1F4BB}" : "\u{1F511}";
|
|
26330
|
-
const rotationMode = getGeminiRotationMode();
|
|
26331
|
-
const rotationLabels = { off: "off", all: "all slots", accounts: "accounts only", keys: "keys only" };
|
|
26332
|
-
const rotationNote = rotationMode === "off" ? "Rotation is off \u2014 only this account will be used." : `Rotation: ${rotationLabels[rotationMode]} (this is the starting slot).`;
|
|
26333
|
-
await channel.sendText(chatId, `${icon} Account set to <b>${label2}</b>
|
|
26334
|
-
${rotationNote}`, { parseMode: "html" });
|
|
26335
26944
|
}
|
|
26336
26945
|
}
|
|
26946
|
+
const slots = getGeminiSlots();
|
|
26947
|
+
const rows = buildAccountSlotKeyboard(slots, getChatGeminiSlotId(chatId), getGeminiRotationMode(), "gslot:", "grotation:");
|
|
26948
|
+
await sendOrEditKeyboard(chatId, channel, messageId, "\u{1F511} Gemini Accounts & Rotation:", rows);
|
|
26337
26949
|
return;
|
|
26338
26950
|
} else if (data === "gopen:accounts") {
|
|
26339
26951
|
const slots = getGeminiSlots();
|
|
26340
26952
|
if (slots.length === 0) {
|
|
26341
|
-
await
|
|
26342
|
-
|
|
26343
|
-
|
|
26344
|
-
|
|
26345
|
-
|
|
26346
|
-
|
|
26347
|
-
getChatGeminiSlotId(chatId),
|
|
26348
|
-
getGeminiRotationMode(),
|
|
26349
|
-
"gslot:",
|
|
26350
|
-
"grotation:"
|
|
26953
|
+
await sendOrEditKeyboard(
|
|
26954
|
+
chatId,
|
|
26955
|
+
channel,
|
|
26956
|
+
messageId,
|
|
26957
|
+
"No Gemini credentials configured.\nAdd with: cc-claw gemini add-key or cc-claw gemini add-account",
|
|
26958
|
+
[[{ label: "\u2190 Back", data: "menu:backend" }]]
|
|
26351
26959
|
);
|
|
26352
|
-
|
|
26960
|
+
return;
|
|
26353
26961
|
}
|
|
26962
|
+
const rows = buildAccountSlotKeyboard(slots, getChatGeminiSlotId(chatId), getGeminiRotationMode(), "gslot:", "grotation:");
|
|
26963
|
+
await sendOrEditKeyboard(chatId, channel, messageId, "\u{1F511} Gemini Accounts & Rotation:", rows);
|
|
26354
26964
|
return;
|
|
26355
26965
|
} else if (data.startsWith("bslot:")) {
|
|
26356
26966
|
const parts = data.split(":");
|
|
26357
26967
|
const backend2 = parts[1];
|
|
26358
26968
|
const val = parts[2];
|
|
26359
|
-
const displayName = backend2 === BACKEND.CLAUDE ? "Claude" : "Codex";
|
|
26360
26969
|
if (val === "auto") {
|
|
26361
26970
|
clearChatBackendSlot(chatId, backend2);
|
|
26362
26971
|
clearSession(chatId);
|
|
26363
|
-
await channel.sendText(chatId, `${displayName} slot set to <b>\u{1F504} auto rotation</b>.`, { parseMode: "html" });
|
|
26364
26972
|
} else {
|
|
26365
26973
|
const slotId = parseInt(val, 10);
|
|
26366
|
-
const
|
|
26367
|
-
const slot = slots.find((s) => s.id === slotId);
|
|
26974
|
+
const slot = getBackendSlots(backend2).find((s) => s.id === slotId);
|
|
26368
26975
|
if (slot) {
|
|
26369
26976
|
pinChatBackendSlot(chatId, backend2, slotId);
|
|
26370
26977
|
clearSession(chatId);
|
|
26371
|
-
const label2 = slot.label || `slot-${slot.id}`;
|
|
26372
|
-
const icon = slot.slotType === "oauth" ? "\u{1F468}\u{1F3FD}\u200D\u{1F4BB}" : "\u{1F511}";
|
|
26373
|
-
await channel.sendText(chatId, `${icon} ${displayName} account set to <b>${label2}</b>`, { parseMode: "html" });
|
|
26374
26978
|
}
|
|
26375
26979
|
}
|
|
26980
|
+
const { getBackendRotationMode: getBackendRotationMode2 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
|
|
26981
|
+
const slots = getBackendSlots(backend2);
|
|
26982
|
+
const rows = buildAccountSlotKeyboard(slots, getChatBackendSlotId(chatId, backend2), getBackendRotationMode2(backend2), `bslot:${backend2}:`, `brotation:${backend2}:`);
|
|
26983
|
+
const displayName = backend2 === BACKEND.CLAUDE ? "Claude" : "Codex";
|
|
26984
|
+
await sendOrEditKeyboard(chatId, channel, messageId, `\u{1F511} ${displayName} Accounts & Rotation:`, rows);
|
|
26376
26985
|
return;
|
|
26377
26986
|
} else if (data.startsWith("paidslot:")) {
|
|
26378
26987
|
const parts = data.split(":");
|
|
@@ -26517,10 +27126,10 @@ ${rotationNote}`, { parseMode: "html" });
|
|
|
26517
27126
|
await channel.sendText(chatId, "Fallback expired. Use /backend to switch manually.", { parseMode: "plain" });
|
|
26518
27127
|
}
|
|
26519
27128
|
} else if (data.startsWith("evolve:")) {
|
|
26520
|
-
await handleEvolveCallback(chatId, data, channel);
|
|
27129
|
+
await handleEvolveCallback(chatId, data, channel, messageId);
|
|
26521
27130
|
return;
|
|
26522
27131
|
} else if (data.startsWith("reflect:")) {
|
|
26523
|
-
await handleReflectCallback(chatId, data, channel);
|
|
27132
|
+
await handleReflectCallback(chatId, data, channel, messageId);
|
|
26524
27133
|
return;
|
|
26525
27134
|
} else if (data.startsWith("council:")) {
|
|
26526
27135
|
const parts = data.split(":");
|
|
@@ -26528,45 +27137,54 @@ ${rotationNote}`, { parseMode: "html" });
|
|
|
26528
27137
|
if (action === "toggle") {
|
|
26529
27138
|
const backend2 = parts[2];
|
|
26530
27139
|
const model2 = parts[3];
|
|
26531
|
-
const
|
|
26532
|
-
const toggleAdapter = getAdapter4(backend2);
|
|
27140
|
+
const toggleAdapter = getAdapter(backend2);
|
|
26533
27141
|
const label2 = toggleAdapter.availableModels[model2]?.label ?? model2;
|
|
26534
27142
|
const { toggleParticipant: toggleParticipant2, buildSelectKeyboard: buildSelectKeyboard2, hasPendingCouncil: hasPendingCouncil2 } = await Promise.resolve().then(() => (init_wizard2(), wizard_exports2));
|
|
26535
27143
|
if (!hasPendingCouncil2(chatId)) {
|
|
26536
|
-
await
|
|
27144
|
+
await sendOrEditKeyboard(
|
|
27145
|
+
chatId,
|
|
27146
|
+
channel,
|
|
27147
|
+
messageId,
|
|
27148
|
+
"No council wizard active. Use /council to start.",
|
|
27149
|
+
[]
|
|
27150
|
+
);
|
|
26537
27151
|
return;
|
|
26538
27152
|
}
|
|
26539
27153
|
toggleParticipant2(chatId, backend2, model2, label2);
|
|
26540
|
-
|
|
26541
|
-
|
|
26542
|
-
await channel.sendKeyboard(chatId, text, buttons);
|
|
26543
|
-
}
|
|
27154
|
+
const { text, buttons } = buildSelectKeyboard2(chatId);
|
|
27155
|
+
await sendOrEditKeyboard(chatId, channel, messageId, text, buttons);
|
|
26544
27156
|
return;
|
|
26545
27157
|
}
|
|
26546
27158
|
if (action === "start") {
|
|
26547
27159
|
const { getCouncilState: getCouncilState2 } = await Promise.resolve().then(() => (init_wizard2(), wizard_exports2));
|
|
26548
27160
|
const state = getCouncilState2(chatId);
|
|
26549
27161
|
if (!state || state.selected.size < 2) {
|
|
26550
|
-
await
|
|
27162
|
+
await sendOrEditKeyboard(chatId, channel, messageId, "Select at least 2 models first.", []);
|
|
26551
27163
|
return;
|
|
26552
27164
|
}
|
|
26553
27165
|
state.step = "question";
|
|
26554
27166
|
const names = [...state.selected.values()].map((p) => p.label).join(", ");
|
|
26555
|
-
await
|
|
27167
|
+
await sendOrEditKeyboard(
|
|
27168
|
+
chatId,
|
|
27169
|
+
channel,
|
|
27170
|
+
messageId,
|
|
27171
|
+
`\u{1F3DB}\uFE0F Council members: ${names}
|
|
26556
27172
|
|
|
26557
|
-
Now type the question you want them to debate.`,
|
|
27173
|
+
Now type the question you want them to debate.`,
|
|
27174
|
+
[[{ label: "\u2715 Cancel", data: "council:cancel" }]]
|
|
27175
|
+
);
|
|
26558
27176
|
return;
|
|
26559
27177
|
}
|
|
26560
27178
|
if (action === "cancel") {
|
|
26561
27179
|
const { cancelCouncil: cancelCouncil2 } = await Promise.resolve().then(() => (init_wizard2(), wizard_exports2));
|
|
26562
27180
|
cancelCouncil2(chatId);
|
|
26563
|
-
await
|
|
27181
|
+
await sendOrEditKeyboard(chatId, channel, messageId, "Council cancelled.", []);
|
|
26564
27182
|
return;
|
|
26565
27183
|
}
|
|
26566
27184
|
if (action === "noop") return;
|
|
26567
27185
|
return;
|
|
26568
27186
|
} else if (data.startsWith("opt:")) {
|
|
26569
|
-
await handleOptimizeCallback(chatId, data, channel);
|
|
27187
|
+
await handleOptimizeCallback(chatId, data, channel, messageId);
|
|
26570
27188
|
return;
|
|
26571
27189
|
} else if (data.startsWith("ollama:")) {
|
|
26572
27190
|
const { handleOllamaCallback: handleOllamaCallback2 } = await Promise.resolve().then(() => (init_ollama3(), ollama_exports2));
|
|
@@ -26648,14 +27266,14 @@ Now type the question you want them to debate.`, { parseMode: "plain" });
|
|
|
26648
27266
|
const rest = data.slice(5);
|
|
26649
27267
|
if (rest.startsWith("recent:")) {
|
|
26650
27268
|
const limit = parseInt(rest.slice(7), 10) || 25;
|
|
26651
|
-
await sendHistoryView(chatId, channel, { limit });
|
|
27269
|
+
await sendHistoryView(chatId, channel, { limit }, messageId);
|
|
26652
27270
|
} else if (rest === "today") {
|
|
26653
|
-
await sendHistoryView(chatId, channel, { period: "today" });
|
|
27271
|
+
await sendHistoryView(chatId, channel, { period: "today" }, messageId);
|
|
26654
27272
|
} else if (rest === "week") {
|
|
26655
|
-
await sendHistoryView(chatId, channel, { period: "week" });
|
|
27273
|
+
await sendHistoryView(chatId, channel, { period: "week" }, messageId);
|
|
26656
27274
|
} else if (rest.startsWith("backend:")) {
|
|
26657
27275
|
const backend2 = rest.slice(8);
|
|
26658
|
-
await sendHistoryView(chatId, channel, { backend: backend2 });
|
|
27276
|
+
await sendHistoryView(chatId, channel, { backend: backend2 }, messageId);
|
|
26659
27277
|
}
|
|
26660
27278
|
return;
|
|
26661
27279
|
} else if (data.startsWith("forget:")) {
|
|
@@ -26667,36 +27285,37 @@ Now type the question you want them to debate.`, { parseMode: "plain" });
|
|
|
26667
27285
|
const id = parseInt(rest.slice(5), 10);
|
|
26668
27286
|
const memory2 = getMemoryById(id);
|
|
26669
27287
|
if (!memory2) {
|
|
26670
|
-
|
|
26671
|
-
|
|
26672
|
-
|
|
26673
|
-
]);
|
|
26674
|
-
} else {
|
|
26675
|
-
await channel.sendText(chatId, "This memory no longer exists.", { parseMode: "plain" });
|
|
26676
|
-
}
|
|
27288
|
+
await sendOrEditKeyboard(chatId, channel, messageId, "This memory no longer exists.", [
|
|
27289
|
+
[{ label: "Refresh List", data: "forget:page:1" }]
|
|
27290
|
+
]);
|
|
26677
27291
|
return;
|
|
26678
27292
|
}
|
|
26679
27293
|
const detail = `Delete this memory?
|
|
27294
|
+
|
|
26680
27295
|
"${memory2.trigger}: ${memory2.content}"
|
|
26681
27296
|
Salience: ${memory2.salience.toFixed(2)} | Created: ${memory2.created_at.slice(0, 10)}`;
|
|
26682
|
-
|
|
26683
|
-
|
|
26684
|
-
|
|
26685
|
-
|
|
26686
|
-
|
|
26687
|
-
|
|
26688
|
-
]);
|
|
26689
|
-
}
|
|
27297
|
+
await sendOrEditKeyboard(chatId, channel, messageId, detail, [
|
|
27298
|
+
[
|
|
27299
|
+
{ label: "\u{1F5D1} Confirm Delete", data: `forget:confirm:${id}`, style: "danger" },
|
|
27300
|
+
{ label: "\u2190 Back", data: "forget:page:1" }
|
|
27301
|
+
]
|
|
27302
|
+
]);
|
|
26690
27303
|
} else if (rest.startsWith("confirm:")) {
|
|
26691
27304
|
const id = parseInt(rest.slice(8), 10);
|
|
26692
27305
|
const deleted = deleteMemoryById(id);
|
|
26693
|
-
|
|
26694
|
-
|
|
26695
|
-
|
|
26696
|
-
|
|
26697
|
-
|
|
27306
|
+
const msg = deleted ? "\u2705 Memory deleted." : "Memory not found \u2014 already deleted.";
|
|
27307
|
+
await sendOrEditKeyboard(chatId, channel, messageId, msg, [
|
|
27308
|
+
[{ label: "\u2190 Back to List", data: "forget:page:1" }]
|
|
27309
|
+
]);
|
|
27310
|
+
} else if (rest.startsWith("keyword:")) {
|
|
27311
|
+
const keyword = rest.slice(8);
|
|
27312
|
+
const count = (await Promise.resolve().then(() => (init_store5(), store_exports5))).forgetMemory(keyword);
|
|
27313
|
+
const msg = count > 0 ? `\u2705 Deleted ${count} memory(ies) matching "${keyword}".` : `No memories found matching "${keyword}".`;
|
|
27314
|
+
await sendOrEditKeyboard(chatId, channel, messageId, msg, [
|
|
27315
|
+
[{ label: "\u2190 Back to List", data: "forget:page:1" }]
|
|
27316
|
+
]);
|
|
26698
27317
|
} else if (rest === "cancel") {
|
|
26699
|
-
await
|
|
27318
|
+
await sendForgetPicker(chatId, channel, 1, messageId);
|
|
26700
27319
|
}
|
|
26701
27320
|
return;
|
|
26702
27321
|
} else if (data.startsWith("mem:")) {
|
|
@@ -26706,60 +27325,17 @@ Salience: ${memory2.salience.toFixed(2)} | Created: ${memory2.created_at.slice(0
|
|
|
26706
27325
|
await sendMemoryPage(chatId, channel, page, messageId);
|
|
26707
27326
|
} else if (rest.startsWith("view:")) {
|
|
26708
27327
|
const id = parseInt(rest.slice(5), 10);
|
|
26709
|
-
|
|
26710
|
-
if (!memory2) {
|
|
26711
|
-
if (typeof channel.sendKeyboard === "function") {
|
|
26712
|
-
await channel.sendKeyboard(chatId, "This memory no longer exists.", [
|
|
26713
|
-
[{ label: "Refresh List", data: "mem:page:1" }]
|
|
26714
|
-
]);
|
|
26715
|
-
} else {
|
|
26716
|
-
await channel.sendText(chatId, "This memory no longer exists.", { parseMode: "plain" });
|
|
26717
|
-
}
|
|
26718
|
-
return;
|
|
26719
|
-
}
|
|
26720
|
-
const detail = [
|
|
26721
|
-
buildSectionHeader(`Memory #${memory2.id}`),
|
|
26722
|
-
`Trigger: ${memory2.trigger}`,
|
|
26723
|
-
`Content: ${memory2.content}`,
|
|
26724
|
-
`Salience: ${memory2.salience.toFixed(2)} | Created: ${memory2.created_at.slice(0, 10)}`
|
|
26725
|
-
].join("\n");
|
|
26726
|
-
if (typeof channel.sendKeyboard === "function") {
|
|
26727
|
-
await channel.sendKeyboard(chatId, detail, [
|
|
26728
|
-
[
|
|
26729
|
-
{ label: "Edit", data: `mem:edit:${id}`, style: "primary" },
|
|
26730
|
-
{ label: "Forget", data: `mem:forget:${id}`, style: "danger" }
|
|
26731
|
-
],
|
|
26732
|
-
[{ label: "Back to List", data: "mem:back" }]
|
|
26733
|
-
]);
|
|
26734
|
-
}
|
|
27328
|
+
await sendMemoryDetail(chatId, id, channel, messageId);
|
|
26735
27329
|
} else if (rest.startsWith("forget:confirm:")) {
|
|
26736
27330
|
const id = parseInt(rest.slice(15), 10);
|
|
26737
27331
|
const deleted = deleteMemoryById(id);
|
|
26738
|
-
|
|
26739
|
-
|
|
26740
|
-
|
|
26741
|
-
|
|
26742
|
-
);
|
|
27332
|
+
const msg = deleted ? "\u2705 Memory deleted." : "Memory not found \u2014 already deleted.";
|
|
27333
|
+
await sendOrEditKeyboard(chatId, channel, messageId, msg, [
|
|
27334
|
+
[{ label: "\u2190 Back to List", data: "mem:back" }]
|
|
27335
|
+
]);
|
|
26743
27336
|
} else if (rest.startsWith("forget:")) {
|
|
26744
27337
|
const id = parseInt(rest.slice(7), 10);
|
|
26745
|
-
|
|
26746
|
-
if (!memory2) {
|
|
26747
|
-
await channel.sendText(chatId, "Memory not found.", { parseMode: "plain" });
|
|
26748
|
-
return;
|
|
26749
|
-
}
|
|
26750
|
-
if (typeof channel.sendKeyboard === "function") {
|
|
26751
|
-
await channel.sendKeyboard(
|
|
26752
|
-
chatId,
|
|
26753
|
-
`Delete this memory?
|
|
26754
|
-
"${memory2.trigger}: ${memory2.content}"`,
|
|
26755
|
-
[
|
|
26756
|
-
[
|
|
26757
|
-
{ label: "Confirm Delete", data: `mem:forget:confirm:${id}`, style: "danger" },
|
|
26758
|
-
{ label: "Cancel", data: `mem:view:${id}` }
|
|
26759
|
-
]
|
|
26760
|
-
]
|
|
26761
|
-
);
|
|
26762
|
-
}
|
|
27338
|
+
await sendMemoryForgetConfirm(chatId, id, channel, messageId);
|
|
26763
27339
|
} else if (rest.startsWith("edit:")) {
|
|
26764
27340
|
const id = parseInt(rest.slice(5), 10);
|
|
26765
27341
|
await channel.sendText(chatId, `Type: /memory edit ${id} <new content>`, { parseMode: "plain" });
|
|
@@ -26772,6 +27348,7 @@ Salience: ${memory2.salience.toFixed(2)} | Created: ${memory2.created_at.slice(0
|
|
|
26772
27348
|
);
|
|
26773
27349
|
const buffer = Buffer.from(lines.join("\n"), "utf-8");
|
|
26774
27350
|
await channel.sendFile(chatId, buffer, "memories.txt", "text/plain");
|
|
27351
|
+
} else if (rest === "noop") {
|
|
26775
27352
|
}
|
|
26776
27353
|
return;
|
|
26777
27354
|
} else if (data.startsWith("hb:")) {
|
|
@@ -26782,54 +27359,102 @@ Salience: ${memory2.salience.toFixed(2)} | Created: ${memory2.created_at.slice(0
|
|
|
26782
27359
|
await sendHeartbeatKeyboard(chatId, channel, messageId);
|
|
26783
27360
|
} else if (rest === "off") {
|
|
26784
27361
|
disableHeartbeat2();
|
|
26785
|
-
await channel.sendText(chatId, "\u26A0\uFE0F Heartbeat paused. Re-enable with /heartbeat on.", { parseMode: "plain" });
|
|
26786
27362
|
await sendHeartbeatKeyboard(chatId, channel, messageId);
|
|
26787
27363
|
} else if (rest.startsWith("interval:")) {
|
|
26788
27364
|
const min = parseInt(rest.slice(9), 10);
|
|
26789
27365
|
if (isNaN(min) || min < 1) return;
|
|
26790
27366
|
updateHeartbeatConfig2({ intervalMs: min * 6e4 });
|
|
26791
27367
|
await sendHeartbeatKeyboard(chatId, channel, messageId);
|
|
26792
|
-
} else if (rest.startsWith("backend:")) {
|
|
26793
|
-
const bid = rest.slice(8);
|
|
26794
|
-
const newBackend = bid === "default" ? null : bid;
|
|
26795
|
-
updateHeartbeatConfig2({ backend: newBackend, model: null });
|
|
26796
|
-
updateHeartbeatField(chatId, "backend", newBackend);
|
|
26797
|
-
updateHeartbeatField(chatId, "model", null);
|
|
26798
|
-
await sendHeartbeatKeyboard(chatId, channel, messageId);
|
|
26799
|
-
} else if (rest.startsWith("model:")) {
|
|
26800
|
-
const model2 = rest.slice(6);
|
|
26801
|
-
const newModel = model2 === "default" ? null : model2;
|
|
26802
|
-
updateHeartbeatConfig2({ model: newModel });
|
|
26803
|
-
updateHeartbeatField(chatId, "model", newModel);
|
|
26804
|
-
await sendHeartbeatKeyboard(chatId, channel, messageId);
|
|
26805
|
-
} else if (rest === "thinking") {
|
|
26806
|
-
await channel.sendText(chatId, "Thinking level for heartbeat: use /editjob to configure.", { parseMode: "plain" });
|
|
26807
27368
|
} else if (rest === "run") {
|
|
26808
|
-
|
|
27369
|
+
if (messageId) {
|
|
27370
|
+
await sendOrEditKeyboard(chatId, channel, messageId, "\u23F3 Running heartbeat check...", []);
|
|
27371
|
+
}
|
|
26809
27372
|
try {
|
|
26810
27373
|
await triggerHb();
|
|
26811
|
-
|
|
27374
|
+
if (messageId) {
|
|
27375
|
+
await sendOrEditKeyboard(chatId, channel, messageId, "\u2705 Heartbeat check complete.", [
|
|
27376
|
+
[{ label: "\u2190 Back to Heartbeat", data: "hb:back" }]
|
|
27377
|
+
]);
|
|
27378
|
+
} else {
|
|
27379
|
+
await channel.sendText(chatId, "\u2705 Heartbeat check complete.", { parseMode: "plain" });
|
|
27380
|
+
}
|
|
26812
27381
|
} catch (err) {
|
|
26813
|
-
|
|
27382
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
27383
|
+
if (messageId) {
|
|
27384
|
+
await sendOrEditKeyboard(chatId, channel, messageId, `\u274C Heartbeat failed: ${errMsg}`, [
|
|
27385
|
+
[{ label: "\u2190 Back to Heartbeat", data: "hb:back" }]
|
|
27386
|
+
]);
|
|
27387
|
+
} else {
|
|
27388
|
+
await channel.sendText(chatId, `\u274C Heartbeat failed: ${errMsg}`, { parseMode: "plain" });
|
|
27389
|
+
}
|
|
26814
27390
|
}
|
|
26815
|
-
} else if (rest === "
|
|
26816
|
-
await
|
|
27391
|
+
} else if (rest === "back") {
|
|
27392
|
+
await sendHeartbeatKeyboard(chatId, channel, messageId);
|
|
27393
|
+
} else if (rest === "engine") {
|
|
27394
|
+
await sendHeartbeatEngine(chatId, channel, messageId);
|
|
27395
|
+
} else if (rest === "fallbacks") {
|
|
27396
|
+
await sendHeartbeatFallbacks(chatId, channel, messageId);
|
|
27397
|
+
} else if (rest === "hours") {
|
|
27398
|
+
await sendHeartbeatHours(chatId, channel, messageId);
|
|
27399
|
+
} else if (rest === "timeout") {
|
|
27400
|
+
await sendHeartbeatTimeout(chatId, channel, messageId);
|
|
27401
|
+
} else if (rest === "thinking") {
|
|
27402
|
+
await sendHeartbeatThinking(chatId, channel, messageId);
|
|
27403
|
+
} else if (rest === "watches") {
|
|
27404
|
+
await sendHeartbeatWatches(chatId, channel, messageId);
|
|
27405
|
+
} else if (rest.startsWith("backend:")) {
|
|
27406
|
+
const bid = rest.slice(8);
|
|
27407
|
+
let defaultModel = null;
|
|
27408
|
+
try {
|
|
27409
|
+
const adapter = getAdapter(bid);
|
|
27410
|
+
defaultModel = adapter.defaultModel;
|
|
27411
|
+
} catch {
|
|
27412
|
+
}
|
|
27413
|
+
updateHeartbeatConfig2({ backend: bid, model: defaultModel, credentialSlotId: null });
|
|
27414
|
+
await sendHeartbeatEngine(chatId, channel, messageId);
|
|
27415
|
+
} else if (rest.startsWith("model:")) {
|
|
27416
|
+
const model2 = rest.slice(6);
|
|
27417
|
+
updateHeartbeatConfig2({ model: model2 });
|
|
27418
|
+
await sendHeartbeatEngine(chatId, channel, messageId);
|
|
27419
|
+
} else if (rest.startsWith("slot:")) {
|
|
27420
|
+
const val = rest.slice(5);
|
|
27421
|
+
const slotId = val === "auto" ? null : parseInt(val, 10);
|
|
27422
|
+
updateHeartbeatConfig2({ credentialSlotId: slotId });
|
|
27423
|
+
await sendHeartbeatEngine(chatId, channel, messageId);
|
|
27424
|
+
} else if (rest.startsWith("hstart:")) {
|
|
27425
|
+
const hour = rest.slice(7);
|
|
27426
|
+
updateHeartbeatField(chatId, "active_start", hour);
|
|
27427
|
+
await sendHeartbeatHours(chatId, channel, messageId);
|
|
27428
|
+
} else if (rest.startsWith("hend:")) {
|
|
27429
|
+
const hour = rest.slice(5);
|
|
27430
|
+
updateHeartbeatField(chatId, "active_end", hour);
|
|
27431
|
+
await sendHeartbeatHours(chatId, channel, messageId);
|
|
27432
|
+
} else if (rest.startsWith("tout:")) {
|
|
27433
|
+
const seconds = parseInt(rest.slice(5), 10);
|
|
27434
|
+
if (!isNaN(seconds)) updateHeartbeatConfig2({ timeout: seconds });
|
|
27435
|
+
await sendHeartbeatTimeout(chatId, channel, messageId);
|
|
27436
|
+
} else if (rest.startsWith("think:")) {
|
|
27437
|
+
const level = rest.slice(6);
|
|
27438
|
+
updateHeartbeatConfig2({ thinking: level === "auto" ? null : level });
|
|
27439
|
+
await sendHeartbeatThinking(chatId, channel, messageId);
|
|
26817
27440
|
} else if (rest.startsWith("fb:add:")) {
|
|
26818
27441
|
const bid = rest.slice(7);
|
|
26819
27442
|
try {
|
|
26820
27443
|
const adapter = getAdapter(bid);
|
|
26821
27444
|
const models = Object.entries(adapter.availableModels);
|
|
26822
27445
|
const rows = [
|
|
26823
|
-
[{ label: `Select model for ${capitalize(bid)} fallback:`, data: "hb:noop" }]
|
|
26824
|
-
[{ label: "Default", data: `hb:fb:pick:${bid}:default`, style: "primary" }]
|
|
27446
|
+
[{ label: `Select model for ${capitalize(bid)} fallback:`, data: "hb:noop" }]
|
|
26825
27447
|
];
|
|
27448
|
+
rows.push([{ label: "Default", data: `hb:fb:pick:${bid}:default`, style: "primary" }]);
|
|
26826
27449
|
if (models.length > 0) {
|
|
26827
|
-
|
|
26828
|
-
|
|
26829
|
-
|
|
26830
|
-
|
|
27450
|
+
for (let i = 0; i < models.length; i += 3) {
|
|
27451
|
+
rows.push(models.slice(i, i + 3).map(([key]) => ({
|
|
27452
|
+
label: shortModelName(key),
|
|
27453
|
+
data: `hb:fb:pick:${bid}:${key}`
|
|
27454
|
+
})));
|
|
27455
|
+
}
|
|
26831
27456
|
}
|
|
26832
|
-
rows.push([{ label: "\u2190 Back", data: "hb:
|
|
27457
|
+
rows.push([{ label: "\u2190 Back", data: "hb:fallbacks" }]);
|
|
26833
27458
|
await sendOrEditKeyboard(
|
|
26834
27459
|
chatId,
|
|
26835
27460
|
channel,
|
|
@@ -26838,30 +27463,34 @@ Salience: ${memory2.salience.toFixed(2)} | Created: ${memory2.created_at.slice(0
|
|
|
26838
27463
|
rows
|
|
26839
27464
|
);
|
|
26840
27465
|
} catch {
|
|
26841
|
-
const
|
|
26842
|
-
const current =
|
|
27466
|
+
const job = findHeartbeatJob2();
|
|
27467
|
+
const current = job?.fallbacks ?? [];
|
|
26843
27468
|
if (!current.some((f) => f.backend === bid)) {
|
|
26844
|
-
|
|
26845
|
-
|
|
27469
|
+
let fbModel = "";
|
|
27470
|
+
try {
|
|
27471
|
+
fbModel = getAdapter(bid).defaultModel;
|
|
27472
|
+
} catch {
|
|
27473
|
+
}
|
|
27474
|
+
current.push({ backend: bid, model: fbModel });
|
|
27475
|
+
updateHeartbeatConfig2({ fallbacks: JSON.stringify(current) });
|
|
26846
27476
|
}
|
|
26847
|
-
await
|
|
27477
|
+
await sendHeartbeatFallbacks(chatId, channel, messageId);
|
|
26848
27478
|
}
|
|
26849
27479
|
} else if (rest.startsWith("fb:pick:")) {
|
|
26850
27480
|
const parts = rest.slice(8).split(":");
|
|
26851
27481
|
const bid = parts[0];
|
|
26852
27482
|
const model2 = parts.slice(1).join(":");
|
|
26853
|
-
const
|
|
26854
|
-
const current =
|
|
27483
|
+
const job = findHeartbeatJob2();
|
|
27484
|
+
const current = [...job?.fallbacks ?? []];
|
|
26855
27485
|
if (!current.some((f) => f.backend === bid)) {
|
|
26856
|
-
|
|
26857
|
-
|
|
27486
|
+
const resolvedModel = model2 === "default" ? "" : model2;
|
|
27487
|
+
current.push({ backend: bid, model: resolvedModel });
|
|
27488
|
+
updateHeartbeatConfig2({ fallbacks: JSON.stringify(current) });
|
|
26858
27489
|
}
|
|
26859
|
-
await
|
|
26860
|
-
} else if (rest === "fb:back") {
|
|
26861
|
-
await sendHeartbeatKeyboard(chatId, channel, messageId);
|
|
27490
|
+
await sendHeartbeatFallbacks(chatId, channel, messageId);
|
|
26862
27491
|
} else if (rest === "fb:clear") {
|
|
26863
|
-
|
|
26864
|
-
await
|
|
27492
|
+
updateHeartbeatConfig2({ fallbacks: null });
|
|
27493
|
+
await sendHeartbeatFallbacks(chatId, channel, messageId);
|
|
26865
27494
|
} else if (rest === "noop") {
|
|
26866
27495
|
}
|
|
26867
27496
|
return;
|
|
@@ -26882,32 +27511,37 @@ Salience: ${memory2.salience.toFixed(2)} | Created: ${memory2.created_at.slice(0
|
|
|
26882
27511
|
} else if (data.startsWith("help:")) {
|
|
26883
27512
|
const rest = data.slice(5);
|
|
26884
27513
|
if (rest === "back") {
|
|
26885
|
-
await sendHelpCategories(chatId, channel);
|
|
27514
|
+
await sendHelpCategories(chatId, channel, messageId);
|
|
26886
27515
|
} else {
|
|
26887
|
-
await sendHelpCategory(chatId, rest, channel);
|
|
27516
|
+
await sendHelpCategory(chatId, rest, channel, messageId);
|
|
26888
27517
|
}
|
|
26889
27518
|
return;
|
|
26890
27519
|
} else if (data.startsWith("usage:")) {
|
|
26891
27520
|
const rest = data.slice(6);
|
|
26892
27521
|
if (rest === "session" || rest === "24h" || rest === "7d" || rest === "30d" || rest === "model") {
|
|
26893
|
-
await sendUnifiedUsage(chatId, channel, rest);
|
|
27522
|
+
await sendUnifiedUsage(chatId, channel, rest, messageId);
|
|
26894
27523
|
} else if (rest === "limits") {
|
|
26895
|
-
await sendUsageLimits(chatId, channel);
|
|
27524
|
+
await sendUsageLimits(chatId, channel, messageId);
|
|
26896
27525
|
} else if (rest === "back") {
|
|
26897
|
-
await sendUnifiedUsage(chatId, channel, "session");
|
|
27526
|
+
await sendUnifiedUsage(chatId, channel, "session", messageId);
|
|
26898
27527
|
} else if (rest === "limits:clear") {
|
|
26899
27528
|
for (const bid of getAllBackendIds()) {
|
|
26900
27529
|
for (const win of ["hourly", "daily", "weekly"]) {
|
|
26901
27530
|
clearBackendLimit(bid, win);
|
|
26902
27531
|
}
|
|
26903
27532
|
}
|
|
26904
|
-
await
|
|
26905
|
-
await sendUnifiedUsage(chatId, channel, "session");
|
|
27533
|
+
await sendUsageLimits(chatId, channel, messageId);
|
|
26906
27534
|
} else if (rest.startsWith("limits:set:")) {
|
|
26907
27535
|
const bid = rest.slice(11);
|
|
26908
|
-
await
|
|
27536
|
+
await sendOrEditKeyboard(
|
|
27537
|
+
chatId,
|
|
27538
|
+
channel,
|
|
27539
|
+
messageId,
|
|
27540
|
+
`Set ${bid} limit via command:
|
|
26909
27541
|
/limits ${bid} daily <tokens>
|
|
26910
|
-
Example: /limits ${bid} daily 500000`,
|
|
27542
|
+
Example: /limits ${bid} daily 500000`,
|
|
27543
|
+
[[{ label: "\u2190 Back to Limits", data: "usage:limits" }]]
|
|
27544
|
+
);
|
|
26911
27545
|
}
|
|
26912
27546
|
return;
|
|
26913
27547
|
} else if (data === "skills:toggle-extract") {
|
|
@@ -27078,9 +27712,10 @@ Use /skills to see all available skills.`, { parseMode: "plain" });
|
|
|
27078
27712
|
if (messageId) await replaceWithText("\u{1F44C} Noted.");
|
|
27079
27713
|
return;
|
|
27080
27714
|
} else if (data === "imp:menu") {
|
|
27081
|
-
|
|
27082
|
-
await channel.sendKeyboard(
|
|
27715
|
+
await sendOrEditKeyboard(
|
|
27083
27716
|
chatId,
|
|
27717
|
+
channel,
|
|
27718
|
+
messageId,
|
|
27084
27719
|
"\u{1F4E5} Import Skill\n\nImport a skill from your coding CLIs into CC-Claw.",
|
|
27085
27720
|
[
|
|
27086
27721
|
[{ label: "\u{1F50D} From CLI Skills", data: "imp:cli", style: "primary" }],
|
|
@@ -27090,10 +27725,12 @@ Use /skills to see all available skills.`, { parseMode: "plain" });
|
|
|
27090
27725
|
);
|
|
27091
27726
|
return;
|
|
27092
27727
|
} else if (data === "imp:search") {
|
|
27093
|
-
await
|
|
27728
|
+
await sendOrEditKeyboard(
|
|
27094
27729
|
chatId,
|
|
27730
|
+
channel,
|
|
27731
|
+
messageId,
|
|
27095
27732
|
"Skill search via Universal Skills Manager is coming in v2.\n\nFor now, use /skill-install <github-url> to install from GitHub.",
|
|
27096
|
-
{
|
|
27733
|
+
[[{ label: "\u2190 Back", data: "imp:menu" }]]
|
|
27097
27734
|
);
|
|
27098
27735
|
return;
|
|
27099
27736
|
} else if (data === "imp:cli" || data.startsWith("imp:cli:")) {
|
|
@@ -27103,18 +27740,16 @@ Use /skills to see all available skills.`, { parseMode: "plain" });
|
|
|
27103
27740
|
(s) => !s.sources.includes("cc-claw") && s.status === "external"
|
|
27104
27741
|
);
|
|
27105
27742
|
if (importable.length === 0) {
|
|
27106
|
-
|
|
27107
|
-
|
|
27108
|
-
|
|
27109
|
-
|
|
27110
|
-
|
|
27111
|
-
|
|
27112
|
-
|
|
27113
|
-
await channel.sendText(chatId, "No new skills found to import.", { parseMode: "plain" });
|
|
27114
|
-
}
|
|
27743
|
+
await sendOrEditKeyboard(
|
|
27744
|
+
chatId,
|
|
27745
|
+
channel,
|
|
27746
|
+
messageId,
|
|
27747
|
+
"No new skills found in your coding CLIs.\n\nSkills are scanned from:\n\u2022 ~/.claude/skills/\n\u2022 ~/.gemini/skills/\n\u2022 ~/.codex/skills/\n\u2022 ~/.cursor/skills/",
|
|
27748
|
+
[[{ label: "\u2190 Back", data: "imp:menu" }]]
|
|
27749
|
+
);
|
|
27115
27750
|
return;
|
|
27116
27751
|
}
|
|
27117
|
-
if (typeof channel.sendKeyboard !== "function") {
|
|
27752
|
+
if (typeof channel.sendKeyboard !== "function" && typeof channel.editKeyboard !== "function") {
|
|
27118
27753
|
const lines = importable.map((s) => `\u2022 ${s.name} [${s.source}]`);
|
|
27119
27754
|
await channel.sendText(chatId, `Found ${importable.length} importable skills:
|
|
27120
27755
|
${lines.join("\n")}`, { parseMode: "plain" });
|
|
@@ -27140,8 +27775,10 @@ ${lines.join("\n")}`, { parseMode: "plain" });
|
|
|
27140
27775
|
buttons.push(navRow);
|
|
27141
27776
|
}
|
|
27142
27777
|
buttons.push([{ label: "\u2190 Back", data: "imp:menu" }]);
|
|
27143
|
-
await
|
|
27778
|
+
await sendOrEditKeyboard(
|
|
27144
27779
|
chatId,
|
|
27780
|
+
channel,
|
|
27781
|
+
messageId,
|
|
27145
27782
|
`Found ${importable.length} skill${importable.length !== 1 ? "s" : ""} in your coding CLIs:`,
|
|
27146
27783
|
buttons
|
|
27147
27784
|
);
|
|
@@ -27164,21 +27801,21 @@ ${lines.join("\n")}`, { parseMode: "plain" });
|
|
|
27164
27801
|
const preview = raw.length > 1500 ? raw.slice(0, 1500) + "\n...(truncated)" : raw;
|
|
27165
27802
|
const escaped = preview.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
27166
27803
|
const collisionNote = existingCcClaw ? "\n\n\u26A0\uFE0F A skill with this name already exists in CC-Claw. Importing will overwrite it." : "";
|
|
27167
|
-
|
|
27168
|
-
|
|
27169
|
-
|
|
27170
|
-
|
|
27804
|
+
await sendOrEditKeyboard(
|
|
27805
|
+
chatId,
|
|
27806
|
+
channel,
|
|
27807
|
+
messageId,
|
|
27808
|
+
`\u{1F4CB} <b>${skillName}</b> [${source}]${collisionNote}
|
|
27171
27809
|
|
|
27172
27810
|
<pre>${escaped}</pre>`,
|
|
27811
|
+
[
|
|
27173
27812
|
[
|
|
27174
|
-
|
|
27175
|
-
|
|
27176
|
-
|
|
27177
|
-
|
|
27178
|
-
|
|
27179
|
-
|
|
27180
|
-
);
|
|
27181
|
-
}
|
|
27813
|
+
{ label: "\u2705 Import & Approve", data: `imp:go:${source}:${skillName}`, style: "success" },
|
|
27814
|
+
{ label: "\u{1F527} Import & Optimize", data: `imp:opt:${source}:${skillName}` }
|
|
27815
|
+
],
|
|
27816
|
+
[{ label: "\u2190 Back", data: "imp:cli" }]
|
|
27817
|
+
]
|
|
27818
|
+
);
|
|
27182
27819
|
return;
|
|
27183
27820
|
} else if (data.startsWith("imp:go:") || data.startsWith("imp:opt:")) {
|
|
27184
27821
|
const doApprove = data.startsWith("imp:go:");
|
|
@@ -27190,12 +27827,12 @@ ${lines.join("\n")}`, { parseMode: "plain" });
|
|
|
27190
27827
|
const allSkills = await discoverAllSkills();
|
|
27191
27828
|
const skill = allSkills.find((s) => s.name === skillName && s.source === source);
|
|
27192
27829
|
if (!skill) {
|
|
27193
|
-
await
|
|
27830
|
+
await sendOrEditKeyboard(chatId, channel, messageId, `Skill "${skillName}" not found.`, [[{ label: "\u2190 Back", data: "imp:cli" }]]);
|
|
27194
27831
|
return;
|
|
27195
27832
|
}
|
|
27196
27833
|
try {
|
|
27197
27834
|
const { readFile: readFileFs } = await import("fs/promises");
|
|
27198
|
-
const { mkdir:
|
|
27835
|
+
const { mkdir: mkdir6, writeFile: writeFileFs } = await import("fs/promises");
|
|
27199
27836
|
const { join: join41 } = await import("path");
|
|
27200
27837
|
const { SKILLS_PATH: SKILLS_PATH2 } = await Promise.resolve().then(() => (init_paths(), paths_exports));
|
|
27201
27838
|
const { updateFrontmatter: updateFrontmatter2, ensureThreeTierFrontmatter: ensureThreeTierFrontmatter2 } = await Promise.resolve().then(() => (init_frontmatter(), frontmatter_exports));
|
|
@@ -27205,30 +27842,32 @@ ${lines.join("\n")}`, { parseMode: "plain" });
|
|
|
27205
27842
|
const targetStatus = doApprove ? "approved" : "imported";
|
|
27206
27843
|
updated = updateFrontmatter2(updated, { status: targetStatus });
|
|
27207
27844
|
const targetDir = join41(SKILLS_PATH2, skillName);
|
|
27208
|
-
await
|
|
27845
|
+
await mkdir6(targetDir, { recursive: true });
|
|
27209
27846
|
await writeFileFs(join41(targetDir, "SKILL.md"), updated, "utf-8");
|
|
27210
27847
|
invalidateSkillCache2();
|
|
27211
27848
|
if (doApprove) {
|
|
27212
|
-
await
|
|
27849
|
+
await sendOrEditKeyboard(
|
|
27213
27850
|
chatId,
|
|
27851
|
+
channel,
|
|
27852
|
+
messageId,
|
|
27214
27853
|
`\u2705 Skill "${skillName}" imported and approved.
|
|
27215
27854
|
|
|
27216
27855
|
It's now available in /skills.`,
|
|
27217
|
-
{
|
|
27856
|
+
[[{ label: "\u2190 Skills", data: "skills:page:1" }, { label: "Import Another", data: "imp:cli" }]]
|
|
27218
27857
|
);
|
|
27219
27858
|
} else {
|
|
27220
|
-
await
|
|
27859
|
+
await sendOrEditKeyboard(
|
|
27221
27860
|
chatId,
|
|
27222
|
-
|
|
27223
|
-
|
|
27224
|
-
Opening optimizer...`,
|
|
27225
|
-
|
|
27861
|
+
channel,
|
|
27862
|
+
messageId,
|
|
27863
|
+
`\u{1F4E5} Skill "${skillName}" imported. Opening optimizer...`,
|
|
27864
|
+
[]
|
|
27226
27865
|
);
|
|
27227
27866
|
const { handleOptimizeCallback: routeOptimize } = await Promise.resolve().then(() => (init_optimize(), optimize_exports));
|
|
27228
27867
|
await routeOptimize(chatId, `opt:skill-pick:${skillName}`, channel);
|
|
27229
27868
|
}
|
|
27230
27869
|
} catch (e) {
|
|
27231
|
-
await
|
|
27870
|
+
await sendOrEditKeyboard(chatId, channel, messageId, `Import failed: ${e.message}`, [[{ label: "\u2190 Back", data: "imp:cli" }]]);
|
|
27232
27871
|
}
|
|
27233
27872
|
return;
|
|
27234
27873
|
} else if (data === "appr:list" || data.startsWith("appr:list:")) {
|
|
@@ -27238,18 +27877,16 @@ Opening optimizer...`,
|
|
|
27238
27877
|
(s) => s.sources.includes("cc-claw") && s.status !== "approved"
|
|
27239
27878
|
);
|
|
27240
27879
|
if (unapproved.length === 0) {
|
|
27241
|
-
|
|
27242
|
-
|
|
27243
|
-
|
|
27244
|
-
|
|
27245
|
-
|
|
27246
|
-
|
|
27247
|
-
|
|
27248
|
-
await channel.sendText(chatId, "All CC-Claw skills are approved.", { parseMode: "plain" });
|
|
27249
|
-
}
|
|
27880
|
+
await sendOrEditKeyboard(
|
|
27881
|
+
chatId,
|
|
27882
|
+
channel,
|
|
27883
|
+
messageId,
|
|
27884
|
+
"\u2705 All CC-Claw skills are approved.",
|
|
27885
|
+
[[{ label: "\u2190 Back to Skills", data: "skills:page:1" }]]
|
|
27886
|
+
);
|
|
27250
27887
|
return;
|
|
27251
27888
|
}
|
|
27252
|
-
if (typeof channel.sendKeyboard !== "function") {
|
|
27889
|
+
if (typeof channel.sendKeyboard !== "function" && typeof channel.editKeyboard !== "function") {
|
|
27253
27890
|
const lines = unapproved.map((s) => `\u2022 ${s.name} [${s.status}]`);
|
|
27254
27891
|
await channel.sendText(chatId, `${unapproved.length} skills need approval:
|
|
27255
27892
|
${lines.join("\n")}`, { parseMode: "plain" });
|
|
@@ -27279,8 +27916,10 @@ ${lines.join("\n")}`, { parseMode: "plain" });
|
|
|
27279
27916
|
{ label: `\u2705 Approve All (${unapproved.length})`, data: "appr:all", style: "success" }
|
|
27280
27917
|
]);
|
|
27281
27918
|
buttons.push([{ label: "\u2190 Back to Skills", data: "skills:page:1" }]);
|
|
27282
|
-
await
|
|
27919
|
+
await sendOrEditKeyboard(
|
|
27283
27920
|
chatId,
|
|
27921
|
+
channel,
|
|
27922
|
+
messageId,
|
|
27284
27923
|
`${unapproved.length} CC-Claw skill${unapproved.length !== 1 ? "s" : ""} need approval.
|
|
27285
27924
|
|
|
27286
27925
|
Select one to review, or approve all:`,
|
|
@@ -27292,11 +27931,11 @@ Select one to review, or approve all:`,
|
|
|
27292
27931
|
const allSkills = await discoverAllSkills();
|
|
27293
27932
|
const skill = allSkills.find((s) => s.name === skillName && s.sources.includes("cc-claw"));
|
|
27294
27933
|
if (!skill) {
|
|
27295
|
-
await
|
|
27934
|
+
await sendOrEditKeyboard(chatId, channel, messageId, `Skill "${skillName}" not found.`, [[{ label: "\u2190 Back", data: "appr:list" }]]);
|
|
27296
27935
|
return;
|
|
27297
27936
|
}
|
|
27298
27937
|
if (skill.status === "approved") {
|
|
27299
|
-
await
|
|
27938
|
+
await sendOrEditKeyboard(chatId, channel, messageId, `"${skillName}" is already approved.`, [[{ label: "\u2190 Back", data: "appr:list" }]]);
|
|
27300
27939
|
return;
|
|
27301
27940
|
}
|
|
27302
27941
|
const { readFile: readFileFs } = await import("fs/promises");
|
|
@@ -27310,28 +27949,28 @@ Select one to review, or approve all:`,
|
|
|
27310
27949
|
\u{1F4CB} Quality check:
|
|
27311
27950
|
${formatValidationReport2(validation)}` : "\n\n\u2705 Quality checks passed.";
|
|
27312
27951
|
const typeLabel = skill.type === "specialist" ? "\u{1F3AF} Specialist" : "\u{1F527} Tool";
|
|
27313
|
-
|
|
27314
|
-
|
|
27315
|
-
|
|
27316
|
-
|
|
27952
|
+
await sendOrEditKeyboard(
|
|
27953
|
+
chatId,
|
|
27954
|
+
channel,
|
|
27955
|
+
messageId,
|
|
27956
|
+
`${typeLabel} <b>${skillName}</b> [${skill.status}]${validationNote}
|
|
27317
27957
|
|
|
27318
27958
|
<pre>${escaped}</pre>`,
|
|
27959
|
+
[
|
|
27319
27960
|
[
|
|
27320
|
-
|
|
27321
|
-
|
|
27322
|
-
|
|
27323
|
-
|
|
27324
|
-
|
|
27325
|
-
|
|
27326
|
-
);
|
|
27327
|
-
}
|
|
27961
|
+
{ label: "\u2705 Approve", data: `appr:do:${skillName}`, style: "success" },
|
|
27962
|
+
{ label: "\u{1F527} Optimize First", data: `appr:opt:${skillName}` }
|
|
27963
|
+
],
|
|
27964
|
+
[{ label: "\u2190 Back", data: "appr:list" }]
|
|
27965
|
+
]
|
|
27966
|
+
);
|
|
27328
27967
|
return;
|
|
27329
27968
|
} else if (data.startsWith("appr:do:")) {
|
|
27330
27969
|
const skillName = data.slice(8);
|
|
27331
27970
|
const allSkills = await discoverAllSkills();
|
|
27332
27971
|
const skill = allSkills.find((s) => s.name === skillName && s.sources.includes("cc-claw"));
|
|
27333
27972
|
if (!skill) {
|
|
27334
|
-
await
|
|
27973
|
+
await sendOrEditKeyboard(chatId, channel, messageId, `Skill "${skillName}" not found.`, [[{ label: "\u2190 Back", data: "appr:list" }]]);
|
|
27335
27974
|
return;
|
|
27336
27975
|
}
|
|
27337
27976
|
try {
|
|
@@ -27343,40 +27982,40 @@ ${formatValidationReport2(validation)}` : "\n\n\u2705 Quality checks passed.";
|
|
|
27343
27982
|
updated = updateFrontmatter2(updated, { status: "approved" });
|
|
27344
27983
|
writeFileSync16(skill.filePath, updated, "utf-8");
|
|
27345
27984
|
invalidateSkillCache2();
|
|
27346
|
-
await channel.sendText(chatId, `\u2705 "${skillName}" approved.`, { parseMode: "plain" });
|
|
27347
27985
|
const refreshedSkills = await discoverAllSkills();
|
|
27348
27986
|
const stillUnapproved = refreshedSkills.filter(
|
|
27349
27987
|
(s) => s.sources.includes("cc-claw") && s.status !== "approved"
|
|
27350
27988
|
);
|
|
27351
27989
|
if (stillUnapproved.length > 0) {
|
|
27352
|
-
|
|
27353
|
-
|
|
27354
|
-
|
|
27355
|
-
|
|
27356
|
-
|
|
27357
|
-
|
|
27358
|
-
|
|
27359
|
-
|
|
27360
|
-
|
|
27361
|
-
|
|
27990
|
+
await sendOrEditKeyboard(
|
|
27991
|
+
chatId,
|
|
27992
|
+
channel,
|
|
27993
|
+
messageId,
|
|
27994
|
+
`\u2705 "${skillName}" approved.
|
|
27995
|
+
|
|
27996
|
+
${stillUnapproved.length} more skill${stillUnapproved.length !== 1 ? "s" : ""} to review.`,
|
|
27997
|
+
[[
|
|
27998
|
+
{ label: "Continue Reviewing", data: "appr:list", style: "primary" },
|
|
27999
|
+
{ label: "\u2190 Done", data: "skills:page:1" }
|
|
28000
|
+
]]
|
|
28001
|
+
);
|
|
27362
28002
|
} else {
|
|
27363
|
-
|
|
27364
|
-
|
|
27365
|
-
|
|
27366
|
-
|
|
27367
|
-
|
|
27368
|
-
|
|
27369
|
-
|
|
28003
|
+
await sendOrEditKeyboard(
|
|
28004
|
+
chatId,
|
|
28005
|
+
channel,
|
|
28006
|
+
messageId,
|
|
28007
|
+
"\u2705 All CC-Claw skills are now approved!",
|
|
28008
|
+
[[{ label: "\u2190 Back to Skills", data: "skills:page:1" }]]
|
|
28009
|
+
);
|
|
27370
28010
|
}
|
|
27371
28011
|
} catch (e) {
|
|
27372
|
-
await
|
|
28012
|
+
await sendOrEditKeyboard(chatId, channel, messageId, `Approve failed: ${e.message}`, [[{ label: "\u2190 Back", data: "appr:list" }]]);
|
|
27373
28013
|
}
|
|
27374
28014
|
return;
|
|
27375
28015
|
} else if (data.startsWith("appr:opt:")) {
|
|
27376
28016
|
const skillName = data.slice(9);
|
|
27377
|
-
await channel.sendText(chatId, `Opening optimizer for "${skillName}"...`, { parseMode: "plain" });
|
|
27378
28017
|
const { handleOptimizeCallback: routeOptimize } = await Promise.resolve().then(() => (init_optimize(), optimize_exports));
|
|
27379
|
-
await routeOptimize(chatId, `opt:skill-pick:${skillName}`, channel);
|
|
28018
|
+
await routeOptimize(chatId, `opt:skill-pick:${skillName}`, channel, messageId);
|
|
27380
28019
|
return;
|
|
27381
28020
|
} else if (data === "appr:all") {
|
|
27382
28021
|
const allSkills = await discoverAllSkills();
|
|
@@ -27412,15 +28051,21 @@ ${formatValidationReport2(validation)}` : "\n\n\u2705 Quality checks passed.";
|
|
|
27412
28051
|
\u26A0\uFE0F ${issues.length} failed:
|
|
27413
28052
|
${issues.join("\n")}`;
|
|
27414
28053
|
}
|
|
27415
|
-
|
|
27416
|
-
|
|
27417
|
-
|
|
27418
|
-
|
|
27419
|
-
|
|
27420
|
-
|
|
27421
|
-
|
|
28054
|
+
await sendOrEditKeyboard(
|
|
28055
|
+
chatId,
|
|
28056
|
+
channel,
|
|
28057
|
+
messageId,
|
|
28058
|
+
msg,
|
|
28059
|
+
[[{ label: "\u2190 Back to Skills", data: "skills:page:1" }]]
|
|
28060
|
+
);
|
|
27422
28061
|
} catch (e) {
|
|
27423
|
-
await
|
|
28062
|
+
await sendOrEditKeyboard(
|
|
28063
|
+
chatId,
|
|
28064
|
+
channel,
|
|
28065
|
+
messageId,
|
|
28066
|
+
`Bulk approve failed: ${e.message}`,
|
|
28067
|
+
[[{ label: "\u2190 Back", data: "appr:list" }]]
|
|
28068
|
+
);
|
|
27424
28069
|
}
|
|
27425
28070
|
return;
|
|
27426
28071
|
} else if (data.startsWith("skill:")) {
|
|
@@ -27455,7 +28100,6 @@ ${issues.join("\n")}`;
|
|
|
27455
28100
|
var init_callbacks = __esm({
|
|
27456
28101
|
"src/router/callbacks.ts"() {
|
|
27457
28102
|
"use strict";
|
|
27458
|
-
init_format();
|
|
27459
28103
|
init_log();
|
|
27460
28104
|
init_agent();
|
|
27461
28105
|
init_discover();
|
|
@@ -28393,7 +29037,8 @@ Debating: "${question.slice(0, 100)}${question.length > 100 ? "\u2026" : ""}"`,
|
|
|
28393
29037
|
}
|
|
28394
29038
|
responseText += `
|
|
28395
29039
|
|
|
28396
|
-
\u{1F9E0} [${shortModel} | ${capitalize(thinking2)}${slotTag}] \u23F1\uFE0F ${elapsedSec}s
|
|
29040
|
+
\u{1F9E0} [${shortModel} | ${capitalize(thinking2)}${slotTag}] \u23F1\uFE0F ${elapsedSec}s
|
|
29041
|
+
\u{1F195} /new | \u{1F9FD} /clear`;
|
|
28397
29042
|
}
|
|
28398
29043
|
if (observedSubagents.size > 0) {
|
|
28399
29044
|
const names = [...observedSubagents].join(", ");
|
|
@@ -28876,8 +29521,8 @@ async function runWithRetry(job, model2, runId, t0) {
|
|
|
28876
29521
|
}
|
|
28877
29522
|
for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
|
|
28878
29523
|
try {
|
|
28879
|
-
const { getVerboseLevel:
|
|
28880
|
-
const vLevel =
|
|
29524
|
+
const { getVerboseLevel: getVerboseLevel4 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
|
|
29525
|
+
const vLevel = getVerboseLevel4(chatId);
|
|
28881
29526
|
let onToolAction;
|
|
28882
29527
|
if (vLevel !== "off") {
|
|
28883
29528
|
const { makeToolActionCallback: makeToolActionCallback2 } = await Promise.resolve().then(() => (init_router2(), router_exports));
|
|
@@ -29835,7 +30480,7 @@ var init_telegram2 = __esm({
|
|
|
29835
30480
|
init_health3();
|
|
29836
30481
|
init_store5();
|
|
29837
30482
|
init_telegram_throttle();
|
|
29838
|
-
FAST_PATH_COMMANDS = /* @__PURE__ */ new Set(["stop", "status", "new", "newchat"]);
|
|
30483
|
+
FAST_PATH_COMMANDS = /* @__PURE__ */ new Set(["stop", "status", "new", "newchat", "clear"]);
|
|
29839
30484
|
TelegramChannel = class _TelegramChannel {
|
|
29840
30485
|
name = "telegram";
|
|
29841
30486
|
bot;
|
|
@@ -29909,6 +30554,7 @@ var init_telegram2 = __esm({
|
|
|
29909
30554
|
{ command: "status", description: "Session, backend, model, and usage" },
|
|
29910
30555
|
{ command: "new", description: "Start a fresh conversation" },
|
|
29911
30556
|
{ command: "newchat", description: "Start a fresh conversation" },
|
|
30557
|
+
{ command: "clear", description: "Clear session without saving (no summary)" },
|
|
29912
30558
|
{ command: "summarize", description: "Save session to memory (or 'all' for pre-restart)" },
|
|
29913
30559
|
{ command: "stop", description: "Cancel the current running task" },
|
|
29914
30560
|
// Backend & model
|
|
@@ -30264,17 +30910,25 @@ var init_telegram2 = __esm({
|
|
|
30264
30910
|
}
|
|
30265
30911
|
}
|
|
30266
30912
|
}
|
|
30267
|
-
|
|
30913
|
+
buildInlineKeyboard(buttons) {
|
|
30268
30914
|
const keyboard = new InlineKeyboard();
|
|
30269
30915
|
for (const row of buttons) {
|
|
30270
30916
|
for (const btn of row) {
|
|
30271
|
-
|
|
30917
|
+
if (btn.switchInlineQueryCurrentChat !== void 0) {
|
|
30918
|
+
keyboard.switchInlineCurrent(btn.label, btn.switchInlineQueryCurrentChat);
|
|
30919
|
+
} else {
|
|
30920
|
+
keyboard.text(btn.label, btn.data);
|
|
30921
|
+
}
|
|
30272
30922
|
if (btn.style === "success") keyboard.success();
|
|
30273
30923
|
else if (btn.style === "danger") keyboard.danger();
|
|
30274
30924
|
else if (btn.style === "primary") keyboard.primary();
|
|
30275
30925
|
}
|
|
30276
30926
|
keyboard.row();
|
|
30277
30927
|
}
|
|
30928
|
+
return keyboard;
|
|
30929
|
+
}
|
|
30930
|
+
async editKeyboard(chatId, messageId, text, buttons) {
|
|
30931
|
+
const keyboard = this.buildInlineKeyboard(buttons);
|
|
30278
30932
|
const formatted = sanitizeForTelegram(formatForTelegram(text));
|
|
30279
30933
|
try {
|
|
30280
30934
|
await this.throttle.send(
|
|
@@ -30314,16 +30968,7 @@ var init_telegram2 = __esm({
|
|
|
30314
30968
|
this.reactionHandlers.push(handler);
|
|
30315
30969
|
}
|
|
30316
30970
|
async sendKeyboard(chatId, text, buttons, threadId) {
|
|
30317
|
-
const keyboard =
|
|
30318
|
-
for (const row of buttons) {
|
|
30319
|
-
for (const btn of row) {
|
|
30320
|
-
keyboard.text(btn.label, btn.data);
|
|
30321
|
-
if (btn.style === "success") keyboard.success();
|
|
30322
|
-
else if (btn.style === "danger") keyboard.danger();
|
|
30323
|
-
else if (btn.style === "primary") keyboard.primary();
|
|
30324
|
-
}
|
|
30325
|
-
keyboard.row();
|
|
30326
|
-
}
|
|
30971
|
+
const keyboard = this.buildInlineKeyboard(buttons);
|
|
30327
30972
|
const MAX_KEYBOARD_TEXT = 4e3;
|
|
30328
30973
|
const safeText = text.length > MAX_KEYBOARD_TEXT ? text.slice(0, MAX_KEYBOARD_TEXT) + "\n\n\u2026(truncated)" : text;
|
|
30329
30974
|
const formatted = sanitizeForTelegram(formatForTelegram(safeText));
|
|
@@ -30550,6 +31195,9 @@ var init_telegram2 = __esm({
|
|
|
30550
31195
|
caption: ctx.message.caption ?? "",
|
|
30551
31196
|
fileName: ctx.message.document.file_id,
|
|
30552
31197
|
mimeType: ctx.message.document.mime_type ?? "application/octet-stream",
|
|
31198
|
+
metadata: {
|
|
31199
|
+
originalName: ctx.message.document.file_name
|
|
31200
|
+
},
|
|
30553
31201
|
chatTitle,
|
|
30554
31202
|
replyToText,
|
|
30555
31203
|
forwardedFrom,
|
|
@@ -35103,9 +35751,9 @@ async function toolsList(globalOpts) {
|
|
|
35103
35751
|
const chatId = resolveChatId2(globalOpts);
|
|
35104
35752
|
const rows = readDb.prepare("SELECT tool, enabled FROM chat_tools WHERE chat_id = ?").all(chatId);
|
|
35105
35753
|
readDb.close();
|
|
35106
|
-
const
|
|
35754
|
+
const ALL_TOOLS4 = ["Read", "Write", "Edit", "Bash", "Glob", "Grep", "WebFetch", "WebSearch", "Agent", "AskUserQuestion"];
|
|
35107
35755
|
const toolMap = new Map(rows.map((r) => [r.tool, !!r.enabled]));
|
|
35108
|
-
const tools2 =
|
|
35756
|
+
const tools2 = ALL_TOOLS4.map((t) => ({ name: t, enabled: toolMap.get(t) ?? true }));
|
|
35109
35757
|
output(tools2, (d) => {
|
|
35110
35758
|
const list = d;
|
|
35111
35759
|
const lines = ["", divider("Tools"), ""];
|
|
@@ -35117,10 +35765,10 @@ async function toolsList(globalOpts) {
|
|
|
35117
35765
|
});
|
|
35118
35766
|
}
|
|
35119
35767
|
async function toolsEnable(globalOpts, name) {
|
|
35120
|
-
await
|
|
35768
|
+
await toggleTool5(globalOpts, name, true);
|
|
35121
35769
|
}
|
|
35122
35770
|
async function toolsDisable(globalOpts, name) {
|
|
35123
|
-
await
|
|
35771
|
+
await toggleTool5(globalOpts, name, false);
|
|
35124
35772
|
}
|
|
35125
35773
|
async function toolsReset(globalOpts) {
|
|
35126
35774
|
const { isDaemonRunning: isDaemonRunning2, apiPost: apiPost2 } = await Promise.resolve().then(() => (init_api_client(), api_client_exports));
|
|
@@ -35139,7 +35787,7 @@ async function toolsReset(globalOpts) {
|
|
|
35139
35787
|
process.exit(1);
|
|
35140
35788
|
}
|
|
35141
35789
|
}
|
|
35142
|
-
async function
|
|
35790
|
+
async function toggleTool5(globalOpts, name, enabled) {
|
|
35143
35791
|
const { isDaemonRunning: isDaemonRunning2, apiPost: apiPost2 } = await Promise.resolve().then(() => (init_api_client(), api_client_exports));
|
|
35144
35792
|
if (!await isDaemonRunning2()) {
|
|
35145
35793
|
outputError("DAEMON_OFFLINE", "CC-Claw daemon is not running.\n\n Start it with: cc-claw service start");
|
|
@@ -36996,6 +37644,7 @@ async function validateBotToken(token) {
|
|
|
36996
37644
|
}
|
|
36997
37645
|
async function setup() {
|
|
36998
37646
|
const TOTAL_STEPS = 6;
|
|
37647
|
+
const DRY_RUN = process.env.CC_CLAW_SETUP_DRY_RUN === "1";
|
|
36999
37648
|
console.log("");
|
|
37000
37649
|
console.log(bold(" \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557"));
|
|
37001
37650
|
console.log(bold(" \u2551 \u2551"));
|
|
@@ -37003,6 +37652,10 @@ async function setup() {
|
|
|
37003
37652
|
console.log(bold(" \u2551 Personal AI Assistant via Telegram \u2551"));
|
|
37004
37653
|
console.log(bold(" \u2551 \u2551"));
|
|
37005
37654
|
console.log(bold(" \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D"));
|
|
37655
|
+
if (DRY_RUN) {
|
|
37656
|
+
console.log("");
|
|
37657
|
+
console.log(yellow(" \u26A0\uFE0F DRY RUN MODE \u2014 nothing will be saved or modified"));
|
|
37658
|
+
}
|
|
37006
37659
|
console.log("");
|
|
37007
37660
|
console.log(" This wizard will walk you through setting up CC-Claw.");
|
|
37008
37661
|
console.log(" It takes about 5 minutes. Let's go!\n");
|
|
@@ -37031,8 +37684,10 @@ async function setup() {
|
|
|
37031
37684
|
console.log(yellow(" No backend CLIs found! Install at least one to use CC-Claw."));
|
|
37032
37685
|
}
|
|
37033
37686
|
console.log("");
|
|
37034
|
-
|
|
37035
|
-
|
|
37687
|
+
if (!DRY_RUN) {
|
|
37688
|
+
for (const dir of [CC_CLAW_HOME, DATA_PATH, LOGS_PATH, SKILLS_PATH, RUNNERS_PATH, AGENTS_PATH]) {
|
|
37689
|
+
if (!existsSync61(dir)) mkdirSync18(dir, { recursive: true });
|
|
37690
|
+
}
|
|
37036
37691
|
}
|
|
37037
37692
|
const env = {};
|
|
37038
37693
|
const envSource = existsSync61(ENV_PATH) ? ENV_PATH : existsSync61(".env") ? ".env" : null;
|
|
@@ -37051,8 +37706,12 @@ async function setup() {
|
|
|
37051
37706
|
console.log(yellow(` Found existing database at ${cwdDb} (${(size / 1024).toFixed(0)}KB)`));
|
|
37052
37707
|
const migrate = await confirm("Copy database to ~/.cc-claw/? (preserves memories & history)", true);
|
|
37053
37708
|
if (migrate) {
|
|
37054
|
-
|
|
37055
|
-
|
|
37709
|
+
if (DRY_RUN) {
|
|
37710
|
+
console.log(dim(` [dry-run] Would copy database to ${DB_PATH}`));
|
|
37711
|
+
} else {
|
|
37712
|
+
copyFileSync5(cwdDb, DB_PATH);
|
|
37713
|
+
console.log(green(` Database copied to ${DB_PATH}`));
|
|
37714
|
+
}
|
|
37056
37715
|
}
|
|
37057
37716
|
console.log("");
|
|
37058
37717
|
}
|
|
@@ -37190,8 +37849,8 @@ async function setup() {
|
|
|
37190
37849
|
console.log("");
|
|
37191
37850
|
console.log(dim(" Choose your transcription provider:\n"));
|
|
37192
37851
|
console.log(" 1. Local Whisper \u2014 free, works offline, runs on your machine");
|
|
37193
|
-
console.log("
|
|
37194
|
-
console.log(" Models downloaded
|
|
37852
|
+
console.log(" No external tools needed \u2014 runs natively in Node.js");
|
|
37853
|
+
console.log(" Models downloaded once (~75MB\u20131.5GB depending on quality)");
|
|
37195
37854
|
console.log("");
|
|
37196
37855
|
console.log(" 2. Groq \u2014 free cloud API, fast, no local install needed");
|
|
37197
37856
|
console.log(" Requires: free Groq API key from https://console.groq.com/keys");
|
|
@@ -37213,7 +37872,22 @@ async function setup() {
|
|
|
37213
37872
|
const modelMap = { "1": "tiny.en", "2": "base.en", "3": "small.en", "4": "small", "5": "medium.en", "6": "medium" };
|
|
37214
37873
|
env.STT_PROVIDER = "local-whisper";
|
|
37215
37874
|
env.STT_MODEL = modelMap[modelChoice] ?? "small.en";
|
|
37216
|
-
|
|
37875
|
+
const selectedModel = env.STT_MODEL;
|
|
37876
|
+
console.log(green(` Local Whisper selected (${selectedModel}).`));
|
|
37877
|
+
if (!DRY_RUN) {
|
|
37878
|
+
console.log(dim(` Model download started in the background. It will be ready by the time you finish setup.`));
|
|
37879
|
+
Promise.resolve().then(() => (init_stt(), stt_exports)).then(({ downloadWhisperModel: downloadWhisperModel2 }) => {
|
|
37880
|
+
downloadWhisperModel2(selectedModel, (msg) => {
|
|
37881
|
+
if (msg.startsWith("\u2705")) console.log(green(` ${msg}`));
|
|
37882
|
+
}).catch((err) => {
|
|
37883
|
+
console.log(yellow(` Background model download failed: ${err.message}`));
|
|
37884
|
+
console.log(dim(` The model will download when you first send a voice message.`));
|
|
37885
|
+
});
|
|
37886
|
+
}).catch(() => {
|
|
37887
|
+
});
|
|
37888
|
+
} else {
|
|
37889
|
+
console.log(dim(` [dry-run] Skipping model download.`));
|
|
37890
|
+
}
|
|
37217
37891
|
} else if (sttChoice === "2") {
|
|
37218
37892
|
const groqKey = await requiredInput("Groq API key", env.GROQ_API_KEY);
|
|
37219
37893
|
env.GROQ_API_KEY = groqKey;
|
|
@@ -37293,14 +37967,29 @@ async function setup() {
|
|
|
37293
37967
|
envLines.push("", "# Video Analysis", `GEMINI_API_KEY=${env.GEMINI_API_KEY}`);
|
|
37294
37968
|
}
|
|
37295
37969
|
const envContent = envLines.join("\n") + "\n";
|
|
37296
|
-
|
|
37297
|
-
|
|
37970
|
+
if (DRY_RUN) {
|
|
37971
|
+
console.log(dim(` [dry-run] Would save config to ${ENV_PATH}`));
|
|
37972
|
+
console.log(dim(" [dry-run] Generated config:"));
|
|
37973
|
+
for (const line of envLines) {
|
|
37974
|
+
if (line.includes("TOKEN") || line.includes("KEY") || line.includes("API")) {
|
|
37975
|
+
const masked = line.replace(/=(.+)$/, "=***");
|
|
37976
|
+
console.log(dim(` ${masked}`));
|
|
37977
|
+
} else {
|
|
37978
|
+
console.log(dim(` ${line}`));
|
|
37979
|
+
}
|
|
37980
|
+
}
|
|
37981
|
+
} else {
|
|
37982
|
+
writeFileSync15(ENV_PATH, envContent, { mode: 384 });
|
|
37983
|
+
console.log(green(` Config saved to ${ENV_PATH} (permissions: owner-only)`));
|
|
37984
|
+
}
|
|
37298
37985
|
header(6, TOTAL_STEPS, "Run on Startup (Daemon)");
|
|
37299
37986
|
console.log(" CC-Claw can run automatically in the background, starting");
|
|
37300
37987
|
console.log(" when your Mac boots. No terminal window needed.\n");
|
|
37301
37988
|
const installDaemon = await confirm("Install CC-Claw as a background service?", true);
|
|
37302
37989
|
if (installDaemon) {
|
|
37303
|
-
|
|
37990
|
+
if (DRY_RUN) {
|
|
37991
|
+
console.log(dim(" [dry-run] Would install background service"));
|
|
37992
|
+
} else try {
|
|
37304
37993
|
const { installService: installService2 } = await Promise.resolve().then(() => (init_service2(), service_exports2));
|
|
37305
37994
|
installService2();
|
|
37306
37995
|
console.log("");
|
|
@@ -37322,7 +38011,7 @@ async function setup() {
|
|
|
37322
38011
|
}
|
|
37323
38012
|
console.log("");
|
|
37324
38013
|
divider2();
|
|
37325
|
-
console.log(bold(" Setup Complete!"));
|
|
38014
|
+
console.log(bold(DRY_RUN ? " Dry Run Complete! (nothing was saved)" : " Setup Complete!"));
|
|
37326
38015
|
divider2();
|
|
37327
38016
|
console.log("");
|
|
37328
38017
|
console.log(" " + green("[ok]") + ` Telegram bot: @${env.TELEGRAM_BOT_TOKEN ? "configured" : "missing"}`);
|
|
@@ -37964,7 +38653,8 @@ program.command("uninstall", { hidden: true }).description("Remove background se
|
|
|
37964
38653
|
const { uninstallService: uninstallService2 } = await Promise.resolve().then(() => (init_service2(), service_exports2));
|
|
37965
38654
|
uninstallService2();
|
|
37966
38655
|
});
|
|
37967
|
-
program.command("setup").description("Interactive configuration wizard").action(async () => {
|
|
38656
|
+
program.command("setup").description("Interactive configuration wizard").option("--dry-run", "Run the wizard without saving anything (demo/test mode)").action(async (opts) => {
|
|
38657
|
+
if (opts.dryRun) process.env.CC_CLAW_SETUP_DRY_RUN = "1";
|
|
37968
38658
|
await Promise.resolve().then(() => (init_setup(), setup_exports));
|
|
37969
38659
|
});
|
|
37970
38660
|
async function run(argv = process.argv) {
|