cc-claw 0.10.1 → 0.11.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.
Files changed (2) hide show
  1. package/dist/cli.js +774 -422
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -72,7 +72,7 @@ var VERSION;
72
72
  var init_version = __esm({
73
73
  "src/version.ts"() {
74
74
  "use strict";
75
- VERSION = true ? "0.10.1" : (() => {
75
+ VERSION = true ? "0.11.0" : (() => {
76
76
  try {
77
77
  return JSON.parse(readFileSync(join2(process.cwd(), "package.json"), "utf-8")).version ?? "unknown";
78
78
  } catch {
@@ -4556,7 +4556,7 @@ function searchContext(userMessage) {
4556
4556
  }
4557
4557
  return null;
4558
4558
  }
4559
- async function assembleBootstrapPrompt(userMessage, tier = "full", chatId, permMode, responseStyle, agentMode) {
4559
+ async function assembleBootstrapPrompt(userMessage, tier = "full", chatId, permMode, responseStyle, agentMode, sideQuestContext) {
4560
4560
  const sections = [];
4561
4561
  if (Date.now() - lastSyncMs >= 5e3) {
4562
4562
  syncNativeCliFiles();
@@ -4580,9 +4580,12 @@ ${ctx}`);
4580
4580
  }
4581
4581
  }
4582
4582
  if (chatId && tier !== "slim" && tier !== "chat") {
4583
- const bridge = consumeContextBridge(chatId);
4584
- if (bridge) {
4585
- sections.push(bridge);
4583
+ if (sideQuestContext) {
4584
+ const bridge = buildContextBridge(sideQuestContext.parentChatId, 15);
4585
+ if (bridge) sections.push(bridge);
4586
+ } else {
4587
+ const bridge = consumeContextBridge(chatId);
4588
+ if (bridge) sections.push(bridge);
4586
4589
  }
4587
4590
  }
4588
4591
  if (tier !== "slim") {
@@ -4620,6 +4623,23 @@ ${ctx}`);
4620
4623
  "[React] Start your response with [REACT:emoji] \u2014 one emoji from the Telegram allowed set matching the message tone. Examples: \u{1F914} question, \u{1FAE1} task/request, \u{1F525} exciting, \u{1F923} funny, \u{1F622} sad, \u{1F4AF} agree, \u{1F389} celebrate, \u{1F468}\u200D\u{1F4BB} coding, \u{1F92F} mind-blown, \u{1F440} interesting, \u{1F91D} thanks, \u{1F60E} chill."
4621
4624
  );
4622
4625
  }
4626
+ if (sideQuestContext) {
4627
+ const { getInFlightMessage: getInFlightMessage3 } = await Promise.resolve().then(() => (init_agent(), agent_exports));
4628
+ const inFlightMsg = getInFlightMessage3(sideQuestContext.parentChatId);
4629
+ const preamble = [
4630
+ `[Side quest context]`,
4631
+ `You are handling a side request while the user's main agent works on another task.`
4632
+ ];
4633
+ if (inFlightMsg) {
4634
+ preamble.push(`The main agent is currently working on: "${inFlightMsg}"`);
4635
+ }
4636
+ preamble.push(
4637
+ `The user's recent conversation history is included above for context.`,
4638
+ `Focus specifically on what the user is asking in THIS message.`,
4639
+ `[End side quest context]`
4640
+ );
4641
+ sections.push(preamble.join("\n"));
4642
+ }
4623
4643
  sections.push(userMessage);
4624
4644
  const result = sections.join("\n\n");
4625
4645
  log(`[bootstrap] Assembled prompt: tier=${tier}, sections=${sections.length}, totalChars=${result.length}`);
@@ -6772,19 +6792,26 @@ async function spawnAnalysis(adapter, model2, prompt) {
6772
6792
  if (!resultText) resultText = accumulatedText;
6773
6793
  return resultText;
6774
6794
  }
6775
- async function runAnalysis(chatId) {
6795
+ async function runAnalysis(chatId, opts = {}) {
6776
6796
  const db3 = getDb();
6777
- const status = getReflectionStatus(db3, chatId);
6778
- if (status === "frozen") {
6779
- log(`[reflection] Skipping analysis for ${chatId} \u2014 reflection is frozen`);
6780
- return [];
6797
+ const { force = false } = opts;
6798
+ if (!force) {
6799
+ const status = getReflectionStatus(db3, chatId);
6800
+ if (status === "frozen") {
6801
+ log(`[reflection] Skipping analysis for ${chatId} \u2014 reflection is frozen`);
6802
+ return [];
6803
+ }
6781
6804
  }
6782
6805
  const signals = getUnprocessedSignals(db3, chatId);
6783
- if (signals.length === 0) {
6806
+ if (!force && signals.length === 0) {
6784
6807
  log(`[reflection] No unprocessed signals for ${chatId}`);
6785
6808
  return [];
6786
6809
  }
6787
6810
  const conversations = getTodayConversations(chatId);
6811
+ if (signals.length === 0 && !conversations) {
6812
+ log(`[reflection] No signals and no conversations for ${chatId} \u2014 nothing to analyze`);
6813
+ return [];
6814
+ }
6788
6815
  const soulMd = readIdentityFile("SOUL.md");
6789
6816
  const userMd = readIdentityFile("USER.md");
6790
6817
  const applied = getAppliedInsights(db3, chatId);
@@ -6819,7 +6846,7 @@ async function runAnalysis(chatId) {
6819
6846
  return [];
6820
6847
  }
6821
6848
  const { adapter, model: model2 } = resolved;
6822
- log(`[reflection] Running analysis via ${adapter.id}:${model2} for chat ${chatId} (${signals.length} signals)`);
6849
+ log(`[reflection] Running analysis via ${adapter.id}:${model2} for chat ${chatId} (${signals.length} signals, force=${force})`);
6823
6850
  let rawOutput;
6824
6851
  try {
6825
6852
  rawOutput = await spawnAnalysis(adapter, model2, prompt);
@@ -6829,13 +6856,12 @@ async function runAnalysis(chatId) {
6829
6856
  }
6830
6857
  if (!rawOutput || rawOutput.includes("NO_INSIGHTS")) {
6831
6858
  log(`[reflection] Analysis returned no insights for ${chatId}`);
6832
- markSignalsProcessed(db3, signals.map((s) => s.id));
6859
+ if (signals.length > 0) markSignalsProcessed(db3, signals.map((s) => s.id));
6833
6860
  return [];
6834
6861
  }
6835
6862
  const parsed = parseAnalysisOutput(rawOutput);
6836
- const signalIdStr = signals.map((s) => s.id).join(",");
6863
+ const signalIdStr = signals.length > 0 ? signals.map((s) => s.id).join(",") : "manual";
6837
6864
  for (const insight of parsed) {
6838
- const insightStatus = insight.category === "codebase" ? "recommendation" : "pending";
6839
6865
  let conflictsWithId = null;
6840
6866
  if (insight.conflictsWith && insight.conflictsWith !== "none") {
6841
6867
  const parsed_id = parseInt(insight.conflictsWith, 10);
@@ -6856,19 +6882,20 @@ async function runAnalysis(chatId) {
6856
6882
  model: model2
6857
6883
  });
6858
6884
  }
6859
- markSignalsProcessed(db3, signals.map((s) => s.id));
6885
+ if (signals.length > 0) markSignalsProcessed(db3, signals.map((s) => s.id));
6860
6886
  aggregateDailyMetrics(db3, chatId);
6861
6887
  logActivity(db3, {
6862
6888
  chatId,
6863
6889
  source: "agent",
6864
6890
  eventType: "reflection_triggered",
6865
- summary: `Reflection analysis produced ${parsed.length} insight(s) from ${signals.length} signal(s)`,
6891
+ summary: `Reflection analysis produced ${parsed.length} insight(s) from ${signals.length} signal(s)${force ? " (manual)" : ""}`,
6866
6892
  detail: {
6867
6893
  signalCount: signals.length,
6868
6894
  insightCount: parsed.length,
6869
6895
  backend: adapter.id,
6870
6896
  model: model2,
6871
- categories: parsed.map((p) => p.category)
6897
+ categories: parsed.map((p) => p.category),
6898
+ forced: force
6872
6899
  }
6873
6900
  });
6874
6901
  log(`[reflection] Analysis complete for ${chatId}: ${parsed.length} insight(s) from ${signals.length} signal(s)`);
@@ -8446,11 +8473,19 @@ var init_detect = __esm({
8446
8473
  var agent_exports = {};
8447
8474
  __export(agent_exports, {
8448
8475
  askAgent: () => askAgent,
8476
+ getInFlightMessage: () => getInFlightMessage,
8449
8477
  isChatBusy: () => isChatBusy,
8478
+ isSyntheticChatId: () => isSyntheticChatId,
8450
8479
  stopAgent: () => stopAgent
8451
8480
  });
8452
8481
  import { spawn as spawn5 } from "child_process";
8453
8482
  import { createInterface as createInterface4 } from "readline";
8483
+ function isSyntheticChatId(chatId) {
8484
+ return chatId.startsWith("sq:") || chatId.startsWith("cron:");
8485
+ }
8486
+ function getInFlightMessage(chatId) {
8487
+ return activeChats.get(chatId)?.userMessage;
8488
+ }
8454
8489
  function killProcessGroup(proc, signal = "SIGTERM") {
8455
8490
  try {
8456
8491
  if (proc.pid) process.kill(-proc.pid, signal);
@@ -8650,12 +8685,16 @@ function spawnQuery(adapter, config2, model2, cancelState, thinkingLevel, timeou
8650
8685
  });
8651
8686
  });
8652
8687
  }
8653
- async function spawnGeminiWithRotation(chatId, adapter, baseConfig, configWithSession, model2, cancelState, thinkingLevel, timeoutMs, maxTurns, rotationMode, opts, onSlotRotation) {
8688
+ async function spawnGeminiWithRotation(chatId, adapter, baseConfig, configWithSession, model2, cancelState, thinkingLevel, timeoutMs, maxTurns, rotationMode, opts, onSlotRotation, parentChatId) {
8654
8689
  const geminiAdapter = adapter;
8655
8690
  const slots = getEligibleGeminiSlots(rotationMode);
8656
8691
  if (slots.length === 0) {
8657
8692
  return spawnQuery(adapter, configWithSession, model2, cancelState, thinkingLevel, timeoutMs, maxTurns, opts);
8658
8693
  }
8694
+ if (parentChatId) {
8695
+ const { env } = geminiAdapter.getEnvForSlot(parentChatId, void 0, rotationMode);
8696
+ return await spawnQuery(adapter, configWithSession, model2, cancelState, thinkingLevel, timeoutMs, maxTurns, { ...opts, envOverride: env });
8697
+ }
8659
8698
  const maxAttempts = Math.min(slots.length, 10);
8660
8699
  let lastError;
8661
8700
  for (let i = 0; i < maxAttempts; i++) {
@@ -8716,17 +8755,19 @@ function askAgent(chatId, userMessage, opts) {
8716
8755
  return withChatLock(chatId, () => askAgentImpl(chatId, userMessage, opts));
8717
8756
  }
8718
8757
  async function askAgentImpl(chatId, userMessage, opts) {
8719
- const { cwd, onStream, model: model2, backend: backend2, permMode, onToolAction, bootstrapTier, timeoutMs, maxTurns, onSlotRotation, agentMode: optsAgentMode, onSubagentActivity } = opts ?? {};
8720
- const adapter = backend2 ? getAdapter(backend2) : getAdapterForChat(chatId);
8721
- const mode = permMode ?? getMode(chatId);
8722
- const responseStyle = getResponseStyle(chatId);
8723
- const thinkingLevel = getThinkingLevel(chatId);
8758
+ const { cwd, onStream, model: model2, backend: backend2, permMode, onToolAction, bootstrapTier, timeoutMs, maxTurns, onSlotRotation, agentMode: optsAgentMode, onSubagentActivity, settingsSourceChatId } = opts ?? {};
8759
+ const settingsChat = settingsSourceChatId ?? chatId;
8760
+ const adapter = backend2 ? getAdapter(backend2) : getAdapterForChat(settingsChat);
8761
+ const mode = permMode ?? getMode(settingsChat);
8762
+ const responseStyle = getResponseStyle(settingsChat);
8763
+ const thinkingLevel = getThinkingLevel(settingsChat);
8724
8764
  const resolvedCwd = cwd ?? WORKSPACE_PATH;
8725
8765
  const tier = bootstrapTier ?? "full";
8726
- const effectiveAgentMode = optsAgentMode ?? getAgentMode(chatId);
8727
- const fullPrompt = await assembleBootstrapPrompt(userMessage, tier, chatId, mode, responseStyle, effectiveAgentMode);
8728
- const existingSessionId = getSessionId(chatId);
8729
- const allowedTools = getEnabledTools(chatId);
8766
+ const effectiveAgentMode = optsAgentMode ?? getAgentMode(settingsChat);
8767
+ const sideQuestCtx = settingsSourceChatId ? { parentChatId: settingsSourceChatId, actualChatId: chatId } : void 0;
8768
+ const fullPrompt = await assembleBootstrapPrompt(userMessage, tier, settingsChat, mode, responseStyle, effectiveAgentMode, sideQuestCtx);
8769
+ const existingSessionId = getSessionId(settingsChat);
8770
+ const allowedTools = getEnabledTools(settingsChat);
8730
8771
  const mcpConfigPath = tier !== "slim" && effectiveAgentMode !== "native" && MCP_CONFIG_FLAG[adapter.id] ? getMcpConfigPath(chatId) : null;
8731
8772
  const baseConfig = adapter.buildSpawnConfig({
8732
8773
  prompt: fullPrompt,
@@ -8756,7 +8797,7 @@ async function askAgentImpl(chatId, userMessage, opts) {
8756
8797
  }
8757
8798
  return cfg;
8758
8799
  })() : baseConfig;
8759
- const cancelState = { cancelled: false };
8800
+ const cancelState = { cancelled: false, userMessage };
8760
8801
  activeChats.set(chatId, cancelState);
8761
8802
  const spawnOpts = { onStream, onToolAction, onSubagentActivity };
8762
8803
  const resolvedModel = model2 ?? adapter.defaultModel;
@@ -8766,7 +8807,7 @@ async function askAgentImpl(chatId, userMessage, opts) {
8766
8807
  try {
8767
8808
  if (useGeminiRotation) {
8768
8809
  const rotationCb = onSlotRotation ? (from, to) => onSlotRotation(chatId, from, to) : void 0;
8769
- result = await spawnGeminiWithRotation(chatId, adapter, baseConfig, configWithSession, resolvedModel, cancelState, thinkingLevel, timeoutMs, maxTurns, rotationMode, spawnOpts, rotationCb);
8810
+ result = await spawnGeminiWithRotation(chatId, adapter, baseConfig, configWithSession, resolvedModel, cancelState, thinkingLevel, timeoutMs, maxTurns, rotationMode, spawnOpts, rotationCb, settingsSourceChatId);
8770
8811
  } else {
8771
8812
  result = await spawnQuery(adapter, configWithSession, resolvedModel, cancelState, thinkingLevel, timeoutMs, maxTurns, spawnOpts);
8772
8813
  }
@@ -8777,7 +8818,7 @@ async function askAgentImpl(chatId, userMessage, opts) {
8777
8818
  clearSession(chatId);
8778
8819
  if (useGeminiRotation) {
8779
8820
  const rotationCb = onSlotRotation ? (from, to) => onSlotRotation(chatId, from, to) : void 0;
8780
- result = await spawnGeminiWithRotation(chatId, adapter, baseConfig, baseConfig, resolvedModel, cancelState, thinkingLevel, timeoutMs, maxTurns, rotationMode, spawnOpts, rotationCb);
8821
+ result = await spawnGeminiWithRotation(chatId, adapter, baseConfig, baseConfig, resolvedModel, cancelState, thinkingLevel, timeoutMs, maxTurns, rotationMode, spawnOpts, rotationCb, settingsSourceChatId);
8781
8822
  } else {
8782
8823
  result = await spawnQuery(adapter, baseConfig, resolvedModel, cancelState, thinkingLevel, timeoutMs, maxTurns, spawnOpts);
8783
8824
  }
@@ -8802,7 +8843,7 @@ async function askAgentImpl(chatId, userMessage, opts) {
8802
8843
  if (cancelState.cancelled) {
8803
8844
  return { text: "Stopped.", usage: { input: result.input, output: result.output, cacheRead: result.cacheRead } };
8804
8845
  }
8805
- if (result.sessionId) {
8846
+ if (result.sessionId && !isSyntheticChatId(chatId)) {
8806
8847
  setSessionId(chatId, result.sessionId);
8807
8848
  }
8808
8849
  if (!result.resultText && result.sawToolEvents) {
@@ -8813,7 +8854,7 @@ async function askAgentImpl(chatId, userMessage, opts) {
8813
8854
  await summarizeSession(chatId);
8814
8855
  clearSession(chatId);
8815
8856
  }
8816
- if (result.resultText) {
8857
+ if (result.resultText && !isSyntheticChatId(chatId)) {
8817
8858
  appendToLog(chatId, userMessage, result.resultText, adapter.id, model2 ?? null, result.sessionId ?? null);
8818
8859
  const AUTO_SUMMARIZE_THRESHOLD = 30;
8819
8860
  const pairCount = tier !== "chat" ? getMessagePairCount(chatId) : 0;
@@ -8944,7 +8985,7 @@ ${responseText.slice(0, 500)}`);
8944
8985
  if (!cleanText) return true;
8945
8986
  if (channelName === "telegram") {
8946
8987
  const parsed = parseTelegramTarget(targetChatId);
8947
- await channel.sendText(parsed.chatId, cleanText, void 0, parsed.threadId);
8988
+ await channel.sendText(parsed.chatId, cleanText, { threadId: parsed.threadId });
8948
8989
  } else {
8949
8990
  await channel.sendText(targetChatId, cleanText);
8950
8991
  }
@@ -8990,7 +9031,7 @@ async function notifyJobFailure(job, errorMessage2, retryInfo) {
8990
9031
  const lines = [`Scheduled job #${job.id} failed: ${errorMessage2}`];
8991
9032
  if (retryInfo) lines.push(retryInfo);
8992
9033
  try {
8993
- await channel.sendText(notifyTarget, lines.join("\n"), "plain");
9034
+ await channel.sendText(notifyTarget, lines.join("\n"), { parseMode: "plain" });
8994
9035
  } catch {
8995
9036
  }
8996
9037
  }
@@ -9002,7 +9043,7 @@ async function notifyJobAutoPaused(job) {
9002
9043
  await channel.sendText(
9003
9044
  job.chatId,
9004
9045
  `Job #${job.id} ("${job.description}") has been auto-paused after ${job.consecutiveFailures} consecutive failures. Use /resume ${job.id} to re-enable.`,
9005
- "plain"
9046
+ { parseMode: "plain" }
9006
9047
  );
9007
9048
  } catch {
9008
9049
  }
@@ -10022,6 +10063,9 @@ var init_telegram = __esm({
10022
10063
  // src/channels/telegram.ts
10023
10064
  import { Bot, InlineKeyboard, InputFile } from "grammy";
10024
10065
  function numericChatId(chatId) {
10066
+ if (chatId.startsWith("sq:") || chatId.startsWith("cron:")) {
10067
+ throw new Error(`Synthetic chatId "${chatId}" passed to Telegram API`);
10068
+ }
10025
10069
  const raw = chatId.includes(":") ? chatId.split(":").pop() : chatId;
10026
10070
  return parseInt(raw);
10027
10071
  }
@@ -10156,7 +10200,7 @@ var init_telegram2 = __esm({
10156
10200
  error("[telegram] Handler error:", err);
10157
10201
  });
10158
10202
  } else {
10159
- this.sendText(chatId, "I can handle text, voice, photos, documents, and videos. This message type isn't supported yet.", "plain").catch(() => {
10203
+ this.sendText(chatId, "I can handle text, voice, photos, documents, and videos. This message type isn't supported yet.", { parseMode: "plain" }).catch(() => {
10160
10204
  });
10161
10205
  }
10162
10206
  });
@@ -10219,12 +10263,14 @@ var init_telegram2 = __esm({
10219
10263
  async sendTyping(chatId) {
10220
10264
  await this.bot.api.sendChatAction(numericChatId(chatId), "typing");
10221
10265
  }
10222
- async sendText(chatId, text, parseMode, threadId) {
10266
+ async sendText(chatId, text, opts) {
10267
+ const { parseMode, threadId, replyToMessageId } = opts ?? {};
10223
10268
  const threadOpts = threadId ? { message_thread_id: threadId } : {};
10269
+ const replyOpts = replyToMessageId ? { reply_parameters: { message_id: replyToMessageId } } : {};
10224
10270
  if (parseMode === "plain") {
10225
10271
  const plainChunks = splitMessage(text);
10226
10272
  for (const chunk of plainChunks) {
10227
- const sent = await this.bot.api.sendMessage(numericChatId(chatId), chunk, { ...threadOpts });
10273
+ const sent = await this.bot.api.sendMessage(numericChatId(chatId), chunk, { ...threadOpts, ...replyOpts });
10228
10274
  this.trackAgentMessage(sent.message_id, chatId);
10229
10275
  }
10230
10276
  return;
@@ -10235,14 +10281,15 @@ var init_telegram2 = __esm({
10235
10281
  try {
10236
10282
  const sent = await this.bot.api.sendMessage(numericChatId(chatId), chunk, {
10237
10283
  parse_mode: "HTML",
10238
- ...threadOpts
10284
+ ...threadOpts,
10285
+ ...replyOpts
10239
10286
  });
10240
10287
  this.trackAgentMessage(sent.message_id, chatId);
10241
10288
  } catch {
10242
10289
  const sent = await this.bot.api.sendMessage(
10243
10290
  numericChatId(chatId),
10244
10291
  chunk.replace(/<[^>]+>/g, ""),
10245
- { ...threadOpts }
10292
+ { ...threadOpts, ...replyOpts }
10246
10293
  );
10247
10294
  this.trackAgentMessage(sent.message_id, chatId);
10248
10295
  }
@@ -10818,7 +10865,7 @@ function hasActiveProfile(chatId) {
10818
10865
  }
10819
10866
  async function startProfileWizard(chatId, channel) {
10820
10867
  activeProfiles.set(chatId, { step: "name" });
10821
- await channel.sendText(chatId, "Let's set up your profile! I'll ask a few quick questions.\n\nWhat's your name?", "plain");
10868
+ await channel.sendText(chatId, "Let's set up your profile! I'll ask a few quick questions.\n\nWhat's your name?", { parseMode: "plain" });
10822
10869
  }
10823
10870
  async function handleProfileText(chatId, text, channel) {
10824
10871
  const state = activeProfiles.get(chatId);
@@ -10833,9 +10880,9 @@ async function handleProfileText(chatId, text, channel) {
10833
10880
  [{ label: "UTC", data: "profile:tz:UTC" }, { label: "Israel", data: "profile:tz:Asia/Jerusalem" }],
10834
10881
  [{ label: "Europe/London", data: "profile:tz:Europe/London" }, { label: "Asia/Tokyo", data: "profile:tz:Asia/Tokyo" }]
10835
10882
  ]);
10836
- await channel.sendText(chatId, "Or type your timezone (e.g. America/Chicago):", "plain");
10883
+ await channel.sendText(chatId, "Or type your timezone (e.g. America/Chicago):", { parseMode: "plain" });
10837
10884
  } else {
10838
- await channel.sendText(chatId, `Hi ${state.name}! What's your timezone? (e.g. America/New_York, UTC, Asia/Jerusalem)`, "plain");
10885
+ await channel.sendText(chatId, `Hi ${state.name}! What's your timezone? (e.g. America/New_York, UTC, Asia/Jerusalem)`, { parseMode: "plain" });
10839
10886
  }
10840
10887
  break;
10841
10888
  case "timezone":
@@ -10843,7 +10890,7 @@ async function handleProfileText(chatId, text, channel) {
10843
10890
  Intl.DateTimeFormat(void 0, { timeZone: text.trim() });
10844
10891
  state.timezone = text.trim();
10845
10892
  } catch {
10846
- await channel.sendText(chatId, "Invalid timezone. Try again (e.g. America/New_York):", "plain");
10893
+ await channel.sendText(chatId, "Invalid timezone. Try again (e.g. America/New_York):", { parseMode: "plain" });
10847
10894
  return;
10848
10895
  }
10849
10896
  state.step = "style";
@@ -10853,7 +10900,7 @@ async function handleProfileText(chatId, text, channel) {
10853
10900
  [{ label: "Casual", data: "profile:style:casual" }, { label: "Formal", data: "profile:style:formal" }]
10854
10901
  ]);
10855
10902
  } else {
10856
- await channel.sendText(chatId, "Communication style? (concise / detailed / casual / formal)", "plain");
10903
+ await channel.sendText(chatId, "Communication style? (concise / detailed / casual / formal)", { parseMode: "plain" });
10857
10904
  }
10858
10905
  break;
10859
10906
  case "style":
@@ -10865,7 +10912,7 @@ async function handleProfileText(chatId, text, channel) {
10865
10912
  [{ label: "Personal assistant", data: "profile:use:personal" }, { label: "All of the above", data: "profile:use:all" }]
10866
10913
  ]);
10867
10914
  } else {
10868
- await channel.sendText(chatId, "Primary use? (coding / research / personal / all)", "plain");
10915
+ await channel.sendText(chatId, "Primary use? (coding / research / personal / all)", { parseMode: "plain" });
10869
10916
  }
10870
10917
  break;
10871
10918
  case "use_case":
@@ -10880,14 +10927,14 @@ async function handleProfileCallback(chatId, data, channel) {
10880
10927
  if (data.startsWith("profile:tz:")) {
10881
10928
  state.timezone = data.slice(11);
10882
10929
  state.step = "style";
10883
- await channel.sendText(chatId, `Timezone: ${state.timezone}`, "plain");
10930
+ await channel.sendText(chatId, `Timezone: ${state.timezone}`, { parseMode: "plain" });
10884
10931
  if (typeof channel.sendKeyboard === "function") {
10885
10932
  await channel.sendKeyboard(chatId, "How should I communicate?", [
10886
10933
  [{ label: "Concise", data: "profile:style:concise" }, { label: "Detailed", data: "profile:style:detailed" }],
10887
10934
  [{ label: "Casual", data: "profile:style:casual" }, { label: "Formal", data: "profile:style:formal" }]
10888
10935
  ]);
10889
10936
  } else {
10890
- await channel.sendText(chatId, "Communication style? (concise / detailed / casual / formal)", "plain");
10937
+ await channel.sendText(chatId, "Communication style? (concise / detailed / casual / formal)", { parseMode: "plain" });
10891
10938
  }
10892
10939
  } else if (data.startsWith("profile:style:")) {
10893
10940
  state.style = data.slice(14);
@@ -10928,7 +10975,7 @@ Style: ${state.style}
10928
10975
  Use: ${state.useCase}
10929
10976
 
10930
10977
  You can edit ~/.cc-claw/identity/USER.md anytime or run /setup-profile again.`,
10931
- "plain"
10978
+ { parseMode: "plain" }
10932
10979
  );
10933
10980
  }
10934
10981
  function extractUserUpdates(text) {
@@ -11743,9 +11790,9 @@ async function startWizard(chatId, input, channel) {
11743
11790
  lines.push(' "every Monday at 10:30am"');
11744
11791
  lines.push(' "every 5 minutes"');
11745
11792
  lines.push(' or a raw cron: "0 9 * * 1-5"');
11746
- await channel.sendText(chatId, lines.join("\n"), "plain");
11793
+ await channel.sendText(chatId, lines.join("\n"), { parseMode: "plain" });
11747
11794
  } else {
11748
- await channel.sendText(chatId, lines.join("\n"), "plain");
11795
+ await channel.sendText(chatId, lines.join("\n"), { parseMode: "plain" });
11749
11796
  await promptTimezone(chatId, channel);
11750
11797
  }
11751
11798
  }
@@ -11758,7 +11805,7 @@ async function handleWizardText(chatId, text, channel) {
11758
11805
  const trimmed = text.trim();
11759
11806
  if (trimmed.toLowerCase() === "keep" && pending.cron) {
11760
11807
  pending.step = "timezone";
11761
- await channel.sendText(chatId, `Keeping schedule: ${pending.cron}`, "plain");
11808
+ await channel.sendText(chatId, `Keeping schedule: ${pending.cron}`, { parseMode: "plain" });
11762
11809
  await promptTimezone(chatId, channel);
11763
11810
  return;
11764
11811
  }
@@ -11766,7 +11813,7 @@ async function handleWizardText(chatId, text, channel) {
11766
11813
  pending.cron = trimmed;
11767
11814
  pending.scheduleType = "cron";
11768
11815
  pending.step = "timezone";
11769
- await channel.sendText(chatId, `Schedule set: ${trimmed}`, "plain");
11816
+ await channel.sendText(chatId, `Schedule set: ${trimmed}`, { parseMode: "plain" });
11770
11817
  await promptTimezone(chatId, channel);
11771
11818
  return;
11772
11819
  }
@@ -11776,13 +11823,13 @@ async function handleWizardText(chatId, text, channel) {
11776
11823
  pending.scheduleType = "cron";
11777
11824
  if (parsed.task && parsed.task !== text) pending.task = parsed.task;
11778
11825
  pending.step = "timezone";
11779
- await channel.sendText(chatId, `Schedule: ${pending.cron}`, "plain");
11826
+ await channel.sendText(chatId, `Schedule: ${pending.cron}`, { parseMode: "plain" });
11780
11827
  await promptTimezone(chatId, channel);
11781
11828
  } else {
11782
11829
  await channel.sendText(chatId, `Couldn't parse that schedule. Try something like:
11783
11830
  "every day at 9am"
11784
11831
  "every 5 minutes"
11785
- or a raw cron expression like "0 9 * * 1-5"`, "plain");
11832
+ or a raw cron expression like "0 9 * * 1-5"`, { parseMode: "plain" });
11786
11833
  }
11787
11834
  break;
11788
11835
  }
@@ -11795,7 +11842,7 @@ async function handleWizardText(chatId, text, channel) {
11795
11842
  pending.step = "backend";
11796
11843
  await promptBackend(chatId, channel);
11797
11844
  } catch {
11798
- await channel.sendText(chatId, `"${tz}" is not a valid timezone. Try again or tap a button.`, "plain");
11845
+ await channel.sendText(chatId, `"${tz}" is not a valid timezone. Try again or tap a button.`, { parseMode: "plain" });
11799
11846
  }
11800
11847
  break;
11801
11848
  }
@@ -11806,7 +11853,7 @@ async function handleWizardText(chatId, text, channel) {
11806
11853
  pending.step = "model";
11807
11854
  await promptModel(chatId, channel);
11808
11855
  } else {
11809
- await channel.sendText(chatId, `Unknown backend. Available: ${getAvailableBackendIds().join(", ")}`, "plain");
11856
+ await channel.sendText(chatId, `Unknown backend. Available: ${getAvailableBackendIds().join(", ")}`, { parseMode: "plain" });
11810
11857
  }
11811
11858
  break;
11812
11859
  }
@@ -11819,7 +11866,7 @@ async function handleWizardText(chatId, text, channel) {
11819
11866
  pending.step = "thinking";
11820
11867
  await promptThinking(chatId, channel);
11821
11868
  } else {
11822
- await channel.sendText(chatId, `Unknown model. Choose: ${Object.keys(adapter.availableModels).join(", ")}`, "plain");
11869
+ await channel.sendText(chatId, `Unknown model. Choose: ${Object.keys(adapter.availableModels).join(", ")}`, { parseMode: "plain" });
11823
11870
  }
11824
11871
  break;
11825
11872
  }
@@ -11837,7 +11884,7 @@ async function handleWizardText(chatId, text, channel) {
11837
11884
  pending.step = "session";
11838
11885
  await promptSession(chatId, channel);
11839
11886
  } else if (isNaN(val) || val < TIMEOUT_MIN_SECONDS || val > TIMEOUT_MAX_SECONDS) {
11840
- await channel.sendText(chatId, `Invalid timeout. Enter a value between ${TIMEOUT_MIN_SECONDS} and ${TIMEOUT_MAX_SECONDS} seconds, or "default" for ${TIMEOUT_DEFAULT_SECONDS}s.`, "plain");
11887
+ await channel.sendText(chatId, `Invalid timeout. Enter a value between ${TIMEOUT_MIN_SECONDS} and ${TIMEOUT_MAX_SECONDS} seconds, or "default" for ${TIMEOUT_DEFAULT_SECONDS}s.`, { parseMode: "plain" });
11841
11888
  } else {
11842
11889
  pending.timeout = val;
11843
11890
  pending.step = "session";
@@ -11852,7 +11899,7 @@ async function handleWizardText(chatId, text, channel) {
11852
11899
  pending.step = "delivery";
11853
11900
  await promptDelivery(chatId, channel);
11854
11901
  } else {
11855
- await channel.sendText(chatId, "Choose: isolated or main", "plain");
11902
+ await channel.sendText(chatId, "Choose: isolated or main", { parseMode: "plain" });
11856
11903
  }
11857
11904
  break;
11858
11905
  }
@@ -11865,13 +11912,13 @@ async function handleWizardText(chatId, text, channel) {
11865
11912
  await promptTarget(chatId, channel);
11866
11913
  } else if (dm === "webhook") {
11867
11914
  pending.step = "target";
11868
- await channel.sendText(chatId, "Enter the webhook URL:", "plain");
11915
+ await channel.sendText(chatId, "Enter the webhook URL:", { parseMode: "plain" });
11869
11916
  } else {
11870
11917
  pending.step = "confirm";
11871
11918
  await promptConfirm(chatId, channel);
11872
11919
  }
11873
11920
  } else {
11874
- await channel.sendText(chatId, "Choose: announce, webhook, or none", "plain");
11921
+ await channel.sendText(chatId, "Choose: announce, webhook, or none", { parseMode: "plain" });
11875
11922
  }
11876
11923
  break;
11877
11924
  }
@@ -11889,13 +11936,13 @@ async function handleWizardText(chatId, text, channel) {
11889
11936
  break;
11890
11937
  }
11891
11938
  default:
11892
- await channel.sendText(chatId, "Please use the buttons to select an option, or type 'cancel' to abort.", "plain");
11939
+ await channel.sendText(chatId, "Please use the buttons to select an option, or type 'cancel' to abort.", { parseMode: "plain" });
11893
11940
  }
11894
11941
  }
11895
11942
  async function handleWizardCallback(chatId, data, channel) {
11896
11943
  const pending = pendingJobs.get(chatId);
11897
11944
  if (!pending) {
11898
- await channel.sendText(chatId, "No active scheduling wizard. Use /schedule to start one.", "plain");
11945
+ await channel.sendText(chatId, "No active scheduling wizard. Use /schedule to start one.", { parseMode: "plain" });
11899
11946
  return;
11900
11947
  }
11901
11948
  resetWizardTimeout(chatId);
@@ -11903,20 +11950,20 @@ async function handleWizardCallback(chatId, data, channel) {
11903
11950
  const tz = data.slice(9);
11904
11951
  pending.timezone = COMMON_TIMEZONES[tz] ?? tz;
11905
11952
  pending.step = "backend";
11906
- await channel.sendText(chatId, `Timezone: ${pending.timezone}`, "plain");
11953
+ await channel.sendText(chatId, `Timezone: ${pending.timezone}`, { parseMode: "plain" });
11907
11954
  await promptBackend(chatId, channel);
11908
11955
  } else if (data.startsWith("sched:backend:")) {
11909
11956
  const backend2 = data.slice(14);
11910
11957
  pending.backend = backend2;
11911
11958
  pending.step = "model";
11912
11959
  const adapter = getAdapter(backend2);
11913
- await channel.sendText(chatId, `Backend: ${adapter.displayName}`, "plain");
11960
+ await channel.sendText(chatId, `Backend: ${adapter.displayName}`, { parseMode: "plain" });
11914
11961
  await promptModel(chatId, channel);
11915
11962
  } else if (data.startsWith("sched:model:")) {
11916
11963
  const model2 = data.slice(12);
11917
11964
  pending.model = model2;
11918
11965
  pending.step = "thinking";
11919
- await channel.sendText(chatId, `Model: ${model2}`, "plain");
11966
+ await channel.sendText(chatId, `Model: ${model2}`, { parseMode: "plain" });
11920
11967
  await promptThinking(chatId, channel);
11921
11968
  } else if (data.startsWith("sched:thinking:")) {
11922
11969
  pending.thinking = data.slice(15);
@@ -11931,7 +11978,7 @@ async function handleWizardCallback(chatId, data, channel) {
11931
11978
  }
11932
11979
  pending.step = "session";
11933
11980
  const label2 = pending.timeout ? `${pending.timeout}s (${Math.round(pending.timeout / 60)} min)` : `Default (${TIMEOUT_DEFAULT_SECONDS}s)`;
11934
- await channel.sendText(chatId, `Timeout: ${label2}`, "plain");
11981
+ await channel.sendText(chatId, `Timeout: ${label2}`, { parseMode: "plain" });
11935
11982
  await promptSession(chatId, channel);
11936
11983
  } else if (data.startsWith("sched:session:")) {
11937
11984
  pending.sessionType = data.slice(14);
@@ -11944,7 +11991,7 @@ async function handleWizardCallback(chatId, data, channel) {
11944
11991
  await promptTarget(chatId, channel);
11945
11992
  } else if (pending.deliveryMode === "webhook") {
11946
11993
  pending.step = "target";
11947
- await channel.sendText(chatId, "Enter the webhook URL:", "plain");
11994
+ await channel.sendText(chatId, "Enter the webhook URL:", { parseMode: "plain" });
11948
11995
  } else {
11949
11996
  pending.step = "confirm";
11950
11997
  await promptConfirm(chatId, channel);
@@ -11953,21 +12000,21 @@ async function handleWizardCallback(chatId, data, channel) {
11953
12000
  pending.target = chatId;
11954
12001
  pending.channel = "telegram";
11955
12002
  pending.step = "confirm";
11956
- await channel.sendText(chatId, "Delivery: this chat", "plain");
12003
+ await channel.sendText(chatId, "Delivery: this chat", { parseMode: "plain" });
11957
12004
  await promptConfirm(chatId, channel);
11958
12005
  } else if (data === "sched:confirm") {
11959
12006
  await finalizeJob(chatId, channel);
11960
12007
  } else if (data === "sched:edit") {
11961
12008
  pending.step = "schedule";
11962
- await channel.sendText(chatId, "Starting over. When should this run?\nSend a schedule or raw cron expression:", "plain");
12009
+ await channel.sendText(chatId, "Starting over. When should this run?\nSend a schedule or raw cron expression:", { parseMode: "plain" });
11963
12010
  } else if (data === "sched:cancel") {
11964
12011
  pendingJobs.delete(chatId);
11965
- await channel.sendText(chatId, "Scheduling cancelled.", "plain");
12012
+ await channel.sendText(chatId, "Scheduling cancelled.", { parseMode: "plain" });
11966
12013
  }
11967
12014
  }
11968
12015
  async function promptTimezone(chatId, channel) {
11969
12016
  if (typeof channel.sendKeyboard !== "function") {
11970
- await channel.sendText(chatId, "Enter a timezone (e.g. America/New_York, UTC, Asia/Jerusalem):", "plain");
12017
+ await channel.sendText(chatId, "Enter a timezone (e.g. America/New_York, UTC, Asia/Jerusalem):", { parseMode: "plain" });
11971
12018
  return;
11972
12019
  }
11973
12020
  const tzButtons = [];
@@ -11983,7 +12030,7 @@ async function promptTimezone(chatId, channel) {
11983
12030
  }
11984
12031
  async function promptBackend(chatId, channel) {
11985
12032
  if (typeof channel.sendKeyboard !== "function") {
11986
- await channel.sendText(chatId, `Enter backend (${getAvailableBackendIds().join(", ")}):`, "plain");
12033
+ await channel.sendText(chatId, `Enter backend (${getAvailableBackendIds().join(", ")}):`, { parseMode: "plain" });
11987
12034
  return;
11988
12035
  }
11989
12036
  const adapters2 = getAvailableAdapters();
@@ -11999,7 +12046,7 @@ async function promptModel(chatId, channel) {
11999
12046
  const adapter = getAdapter(pending.backend);
12000
12047
  if (typeof channel.sendKeyboard !== "function") {
12001
12048
  const models = Object.keys(adapter.availableModels).join(", ");
12002
- await channel.sendText(chatId, `Enter model (${models}):`, "plain");
12049
+ await channel.sendText(chatId, `Enter model (${models}):`, { parseMode: "plain" });
12003
12050
  return;
12004
12051
  }
12005
12052
  const buttons = Object.entries(adapter.availableModels).map(([id, info]) => [{
@@ -12015,7 +12062,7 @@ async function promptThinking(chatId, channel) {
12015
12062
  const modelInfo = adapter.availableModels[pending.model];
12016
12063
  if (modelInfo?.thinking === "adjustable" && modelInfo.thinkingLevels) {
12017
12064
  if (typeof channel.sendKeyboard !== "function") {
12018
- await channel.sendText(chatId, `Enter thinking level (${modelInfo.thinkingLevels.join(", ")}):`, "plain");
12065
+ await channel.sendText(chatId, `Enter thinking level (${modelInfo.thinkingLevels.join(", ")}):`, { parseMode: "plain" });
12019
12066
  return;
12020
12067
  }
12021
12068
  const buttons = modelInfo.thinkingLevels.map((level) => [{
@@ -12031,7 +12078,7 @@ async function promptThinking(chatId, channel) {
12031
12078
  }
12032
12079
  async function promptTimeout(chatId, channel) {
12033
12080
  if (typeof channel.sendKeyboard !== "function") {
12034
- await channel.sendText(chatId, `Job timeout in seconds (${TIMEOUT_MIN_SECONDS}-${TIMEOUT_MAX_SECONDS}), or "default" for ${TIMEOUT_DEFAULT_SECONDS}s:`, "plain");
12081
+ await channel.sendText(chatId, `Job timeout in seconds (${TIMEOUT_MIN_SECONDS}-${TIMEOUT_MAX_SECONDS}), or "default" for ${TIMEOUT_DEFAULT_SECONDS}s:`, { parseMode: "plain" });
12035
12082
  return;
12036
12083
  }
12037
12084
  await channel.sendKeyboard(chatId, "Maximum runtime for this job?", [
@@ -12048,7 +12095,7 @@ async function promptTimeout(chatId, channel) {
12048
12095
  }
12049
12096
  async function promptSession(chatId, channel) {
12050
12097
  if (typeof channel.sendKeyboard !== "function") {
12051
- await channel.sendText(chatId, "Session type: isolated (fresh, no history) or main (uses conversation context)?", "plain");
12098
+ await channel.sendText(chatId, "Session type: isolated (fresh, no history) or main (uses conversation context)?", { parseMode: "plain" });
12052
12099
  return;
12053
12100
  }
12054
12101
  await channel.sendKeyboard(chatId, "Session type for this job?", [
@@ -12058,7 +12105,7 @@ async function promptSession(chatId, channel) {
12058
12105
  }
12059
12106
  async function promptDelivery(chatId, channel) {
12060
12107
  if (typeof channel.sendKeyboard !== "function") {
12061
- await channel.sendText(chatId, "Delivery mode: announce (send to chat), webhook (HTTP POST), or none (silent)?", "plain");
12108
+ await channel.sendText(chatId, "Delivery mode: announce (send to chat), webhook (HTTP POST), or none (silent)?", { parseMode: "plain" });
12062
12109
  return;
12063
12110
  }
12064
12111
  await channel.sendKeyboard(chatId, "How should results be delivered?", [
@@ -12069,13 +12116,13 @@ async function promptDelivery(chatId, channel) {
12069
12116
  }
12070
12117
  async function promptTarget(chatId, channel) {
12071
12118
  if (typeof channel.sendKeyboard !== "function") {
12072
- await channel.sendText(chatId, "Enter the chat ID or group ID for delivery (or send 'this' for this chat):", "plain");
12119
+ await channel.sendText(chatId, "Enter the chat ID or group ID for delivery (or send 'this' for this chat):", { parseMode: "plain" });
12073
12120
  return;
12074
12121
  }
12075
12122
  await channel.sendKeyboard(chatId, "Where should results be sent?", [
12076
12123
  [{ label: "This chat", data: "sched:target:this" }]
12077
12124
  ]);
12078
- await channel.sendText(chatId, "Or send a chat/group ID (e.g. -100123456789 or -100123456789:topic:42):", "plain");
12125
+ await channel.sendText(chatId, "Or send a chat/group ID (e.g. -100123456789 or -100123456789:topic:42):", { parseMode: "plain" });
12079
12126
  }
12080
12127
  async function promptConfirm(chatId, channel) {
12081
12128
  const pending = pendingJobs.get(chatId);
@@ -12106,7 +12153,7 @@ async function promptConfirm(chatId, channel) {
12106
12153
  [{ label: "Edit", data: "sched:edit" }, { label: "Cancel", data: "sched:cancel" }]
12107
12154
  ]);
12108
12155
  } else {
12109
- await channel.sendText(chatId, lines.join("\n") + "\n\nSend 'confirm' to create or 'cancel' to abort.", "plain");
12156
+ await channel.sendText(chatId, lines.join("\n") + "\n\nSend 'confirm' to create or 'cancel' to abort.", { parseMode: "plain" });
12110
12157
  }
12111
12158
  }
12112
12159
  async function finalizeJob(chatId, channel) {
@@ -12144,7 +12191,7 @@ async function finalizeJob(chatId, channel) {
12144
12191
  Task: ${jobParams.description}
12145
12192
  Schedule: ${jobParams.cron ?? "N/A"}
12146
12193
  Timezone: ${jobParams.timezone}`,
12147
- "plain"
12194
+ { parseMode: "plain" }
12148
12195
  );
12149
12196
  } else {
12150
12197
  const job = insertJob({ ...jobParams, chatId });
@@ -12159,17 +12206,17 @@ Task: ${job.description}
12159
12206
  Schedule: ${job.cron ?? "N/A"}
12160
12207
  Timezone: ${job.timezone}
12161
12208
  Next run will be at the scheduled time.`,
12162
- "plain"
12209
+ { parseMode: "plain" }
12163
12210
  );
12164
12211
  }
12165
12212
  } catch (err) {
12166
- await channel.sendText(chatId, `Failed to ${editJobId ? "update" : "create"} job: ${errorMessage(err)}`, "plain");
12213
+ await channel.sendText(chatId, `Failed to ${editJobId ? "update" : "create"} job: ${errorMessage(err)}`, { parseMode: "plain" });
12167
12214
  }
12168
12215
  }
12169
12216
  async function startEditWizard(chatId, jobId, channel) {
12170
12217
  const job = getJobById(jobId);
12171
12218
  if (!job) {
12172
- await channel.sendText(chatId, `Job #${jobId} not found.`, "plain");
12219
+ await channel.sendText(chatId, `Job #${jobId} not found.`, { parseMode: "plain" });
12173
12220
  return;
12174
12221
  }
12175
12222
  const pending = {
@@ -12201,7 +12248,7 @@ Current schedule: ${schedule2}
12201
12248
  Current task: ${job.description}
12202
12249
 
12203
12250
  Send a new schedule, or type 'keep' to keep the current one:`,
12204
- "plain"
12251
+ { parseMode: "plain" }
12205
12252
  );
12206
12253
  }
12207
12254
  var WIZARD_TIMEOUT_MS, wizardTimers, pendingJobs;
@@ -12521,22 +12568,22 @@ async function handleBackendCommand(command, chatId, channel) {
12521
12568
  try {
12522
12569
  adapter = getAdapterForChat(chatId);
12523
12570
  } catch {
12524
- await channel.sendText(chatId, "No backend set. Use /backend first.", "plain");
12571
+ await channel.sendText(chatId, "No backend set. Use /backend first.", { parseMode: "plain" });
12525
12572
  return;
12526
12573
  }
12527
12574
  const sessionId = getSessionId(chatId);
12528
12575
  if (!sessionId) {
12529
- await channel.sendText(chatId, "No active session. Start a conversation first.", "plain");
12576
+ await channel.sendText(chatId, "No active session. Start a conversation first.", { parseMode: "plain" });
12530
12577
  return;
12531
12578
  }
12532
12579
  const cwd = getCwd(chatId) ?? `${process.env.HOME ?? "/tmp"}/.cc-claw/workspace`;
12533
12580
  try {
12534
12581
  const output2 = await spawnBackendCommand(adapter, command, sessionId, cwd);
12535
12582
  const formatted = buildNativeResponse(adapter.id, command, output2);
12536
- await channel.sendText(chatId, formatted, "html");
12583
+ await channel.sendText(chatId, formatted, { parseMode: "html" });
12537
12584
  } catch (err) {
12538
12585
  const msg = err instanceof Error ? err.message : String(err);
12539
- await channel.sendText(chatId, `Backend command failed: ${msg}`, "plain");
12586
+ await channel.sendText(chatId, `Backend command failed: ${msg}`, { parseMode: "plain" });
12540
12587
  }
12541
12588
  }
12542
12589
  function spawnBackendCommand(adapter, command, sessionId, cwd) {
@@ -12620,6 +12667,7 @@ var init_pagination = __esm({
12620
12667
  var propose_exports = {};
12621
12668
  __export(propose_exports, {
12622
12669
  buildEvolveMenuKeyboard: () => buildEvolveMenuKeyboard,
12670
+ buildEvolveOnboardingKeyboard: () => buildEvolveOnboardingKeyboard,
12623
12671
  buildModelKeyboard: () => buildModelKeyboard,
12624
12672
  buildProposalKeyboard: () => buildProposalKeyboard,
12625
12673
  buildUndoKeyboard: () => buildUndoKeyboard,
@@ -12699,20 +12747,27 @@ function buildProposalKeyboard(insightId, category) {
12699
12747
  ]
12700
12748
  ];
12701
12749
  }
12702
- function buildEvolveMenuKeyboard() {
12750
+ function buildEvolveOnboardingKeyboard() {
12751
+ return [
12752
+ [{ label: "\u2705 Enable Self-Learning", data: "evolve:toggle", style: "success" }],
12753
+ [{ label: "One-Time Analysis", data: "evolve:analyze" }]
12754
+ ];
12755
+ }
12756
+ function buildEvolveMenuKeyboard(ctx) {
12757
+ const reviewLabel = ctx.pendingProposals > 0 ? `Review (${ctx.pendingProposals})` : "Review";
12703
12758
  return [
12704
12759
  [
12705
- { label: "Analyze Now", data: "evolve:analyze", style: "success" },
12706
- { label: "Review Proposals", data: "evolve:review" }
12760
+ { label: "\u{1F50D} Analyze Now", data: "evolve:analyze", style: "success" },
12761
+ ctx.pendingProposals > 0 ? { label: reviewLabel, data: "evolve:review", style: "primary" } : { label: reviewLabel, data: "evolve:review" }
12707
12762
  ],
12708
12763
  [
12709
12764
  { label: "Stats", data: "evolve:stats" },
12710
12765
  { label: "History", data: "evolve:history" }
12711
12766
  ],
12712
12767
  [
12713
- { label: "On/Off", data: "evolve:toggle" },
12768
+ { label: "Model", data: "evolve:model" },
12714
12769
  { label: "Undo", data: "evolve:undo", style: "danger" },
12715
- { label: "Model", data: "evolve:model" }
12770
+ { label: "\u23F8 Disable", data: "evolve:toggle", style: "danger" }
12716
12771
  ]
12717
12772
  ];
12718
12773
  }
@@ -12743,6 +12798,7 @@ var init_propose = __esm({
12743
12798
 
12744
12799
  // src/router.ts
12745
12800
  import { readFile as readFile5, writeFile as writeFile3, unlink as unlink2, mkdir as mkdir2, readdir as readdir3, stat } from "fs/promises";
12801
+ import { randomUUID as randomUUID3 } from "crypto";
12746
12802
  import { resolve as resolvePath, join as join19 } from "path";
12747
12803
  function parseMcpListOutput(output2) {
12748
12804
  const results = [];
@@ -12941,7 +12997,7 @@ async function sendUnifiedUsage(chatId, channel, view) {
12941
12997
  ];
12942
12998
  await channel.sendKeyboard(chatId, lines.join("\n"), buttons);
12943
12999
  } else {
12944
- await channel.sendText(chatId, lines.join("\n"), "plain");
13000
+ await channel.sendText(chatId, lines.join("\n"), { parseMode: "plain" });
12945
13001
  }
12946
13002
  }
12947
13003
  async function sendUsageLimits(chatId, channel) {
@@ -12975,7 +13031,7 @@ async function sendUsageLimits(chatId, channel) {
12975
13031
  ]);
12976
13032
  await channel.sendKeyboard(chatId, lines.join("\n"), rows);
12977
13033
  } else {
12978
- await channel.sendText(chatId, lines.join("\n"), "plain");
13034
+ await channel.sendText(chatId, lines.join("\n"), { parseMode: "plain" });
12979
13035
  }
12980
13036
  }
12981
13037
  function getMediaRetentionMs() {
@@ -13034,6 +13090,22 @@ function pickFallbackEmoji(userMessage) {
13034
13090
  }
13035
13091
  return "\u{1F44D}";
13036
13092
  }
13093
+ function parseSideQuestPrefix(text) {
13094
+ const match = text.match(/^(?:sq|btw):\s*/i);
13095
+ if (match) return { isSideQuest: true, cleanText: text.slice(match[0].length) };
13096
+ return { isSideQuest: false, cleanText: text };
13097
+ }
13098
+ function getActiveSideQuestCount(chatId) {
13099
+ return activeSideQuests.get(chatId)?.size ?? 0;
13100
+ }
13101
+ function stopAllSideQuests(chatId) {
13102
+ const active = activeSideQuests.get(chatId);
13103
+ if (active) {
13104
+ for (const sqId of active) {
13105
+ stopAgent(sqId);
13106
+ }
13107
+ }
13108
+ }
13037
13109
  async function handleResponseExhaustion(responseText, chatId, msg, channel) {
13038
13110
  const raw = responseText.replace(/\n\n🧠 \[.+$/, "").trim();
13039
13111
  if (raw.length > 300 || !isExhaustedMessage(raw)) return false;
@@ -13122,7 +13194,7 @@ async function handleCommand(msg, channel) {
13122
13194
  ([cat, cmds]) => `${cat}:
13123
13195
  ${cmds.map((c) => ` ${c.cmd} \u2014 ${c.desc}`).join("\n")}`
13124
13196
  ).join("\n\n"),
13125
- "plain"
13197
+ { parseMode: "plain" }
13126
13198
  );
13127
13199
  }
13128
13200
  break;
@@ -13141,17 +13213,18 @@ ${cmds.map((c) => ` ${c.cmd} \u2014 ${c.desc}`).join("\n")}`
13141
13213
  await channel.sendText(
13142
13214
  chatId,
13143
13215
  "CC-Claw Menu:\n/newchat \xB7 /status \xB7 /backend \xB7 /model\n/jobs \xB7 /memory \xB7 /history \xB7 /help",
13144
- "plain"
13216
+ { parseMode: "plain" }
13145
13217
  );
13146
13218
  }
13147
13219
  break;
13148
13220
  }
13149
13221
  case "stop": {
13150
13222
  const stopped = stopAgent(chatId);
13223
+ stopAllSideQuests(chatId);
13151
13224
  await channel.sendText(
13152
13225
  chatId,
13153
13226
  stopped ? "Stopping current task..." : "Nothing is running.",
13154
- "plain"
13227
+ { parseMode: "plain" }
13155
13228
  );
13156
13229
  if (stopped && typeof channel.sendKeyboard === "function") {
13157
13230
  await channel.sendKeyboard(chatId, "", [
@@ -13174,7 +13247,7 @@ ${cmds.map((c) => ` ${c.cmd} \u2014 ${c.desc}`).join("\n")}`
13174
13247
  for (const [id, label2] of Object.entries(PERM_MODES)) {
13175
13248
  lines.push(`${id === currentMode ? "\u2713 " : " "}/permissions ${id} \u2014 ${label2}`);
13176
13249
  }
13177
- await channel.sendText(chatId, lines.join("\n"), "plain");
13250
+ await channel.sendText(chatId, lines.join("\n"), { parseMode: "plain" });
13178
13251
  }
13179
13252
  break;
13180
13253
  }
@@ -13191,7 +13264,7 @@ ${cmds.map((c) => ` ${c.cmd} \u2014 ${c.desc}`).join("\n")}`
13191
13264
  const lines = Object.entries(VERBOSE_LEVELS).map(
13192
13265
  ([id, label2]) => `${id === currentVerbose ? "\u2713 " : " "}${id} \u2014 ${label2}`
13193
13266
  );
13194
- await channel.sendText(chatId, ["Tool visibility:", "", ...lines].join("\n"), "plain");
13267
+ await channel.sendText(chatId, ["Tool visibility:", "", ...lines].join("\n"), { parseMode: "plain" });
13195
13268
  }
13196
13269
  break;
13197
13270
  }
@@ -13218,12 +13291,13 @@ Tap to toggle:`,
13218
13291
  );
13219
13292
  } else {
13220
13293
  const lines = ALL_TOOLS.map((t) => `${toolsMap[t] ? "[on] " : "[off]"} ${t}`);
13221
- await channel.sendText(chatId, ["Allowed tools:", "", ...lines].join("\n"), "plain");
13294
+ await channel.sendText(chatId, ["Allowed tools:", "", ...lines].join("\n"), { parseMode: "plain" });
13222
13295
  }
13223
13296
  break;
13224
13297
  }
13225
13298
  case "new":
13226
13299
  case "newchat": {
13300
+ stopAllSideQuests(chatId);
13227
13301
  const oldSessionId = getSessionId(chatId);
13228
13302
  const exchangeCount = getMessagePairCount(chatId);
13229
13303
  const summarized = await summarizeSession(chatId);
@@ -13247,7 +13321,7 @@ Tap to toggle:`,
13247
13321
  }
13248
13322
  } else {
13249
13323
  const msg2 = summarized ? "Session summarized and saved. Fresh conversation started!" : "Fresh conversation started. What's on your mind?";
13250
- await channel.sendText(chatId, msg2, "plain");
13324
+ await channel.sendText(chatId, msg2, { parseMode: "plain" });
13251
13325
  }
13252
13326
  break;
13253
13327
  }
@@ -13255,12 +13329,12 @@ Tap to toggle:`,
13255
13329
  if (commandArgs?.toLowerCase() === "all") {
13256
13330
  const pendingIds = getLoggedChatIds();
13257
13331
  if (pendingIds.length === 0) {
13258
- await channel.sendText(chatId, "No pending sessions to summarize.", "plain");
13332
+ await channel.sendText(chatId, "No pending sessions to summarize.", { parseMode: "plain" });
13259
13333
  break;
13260
13334
  }
13261
- await channel.sendText(chatId, `Summarizing ${pendingIds.length} pending session(s)...`, "plain");
13335
+ await channel.sendText(chatId, `Summarizing ${pendingIds.length} pending session(s)...`, { parseMode: "plain" });
13262
13336
  await summarizeAllPending();
13263
- await channel.sendText(chatId, `Done. ${pendingIds.length} session(s) summarized and saved to memory.`, "plain");
13337
+ await channel.sendText(chatId, `Done. ${pendingIds.length} session(s) summarized and saved to memory.`, { parseMode: "plain" });
13264
13338
  } else {
13265
13339
  const pairs = getMessagePairCount(chatId);
13266
13340
  if (pairs < 2) {
@@ -13268,16 +13342,16 @@ Tap to toggle:`,
13268
13342
  await channel.sendText(
13269
13343
  chatId,
13270
13344
  "Session log was cleared (auto-compact or service restart). Your conversation history is preserved in episodic memory. Continue chatting normally, or use /newchat to start fresh.",
13271
- "plain"
13345
+ { parseMode: "plain" }
13272
13346
  );
13273
13347
  } else {
13274
- await channel.sendText(chatId, "Not enough conversation to summarize (need at least 2 exchanges).", "plain");
13348
+ await channel.sendText(chatId, "Not enough conversation to summarize (need at least 2 exchanges).", { parseMode: "plain" });
13275
13349
  }
13276
13350
  break;
13277
13351
  }
13278
13352
  const progressMsgId = await channel.sendTextReturningId?.(chatId, `Summarizing ${pairs} exchanges...`, "plain");
13279
13353
  if (!progressMsgId) {
13280
- await channel.sendText(chatId, `Summarizing ${pairs} exchanges...`, "plain");
13354
+ await channel.sendText(chatId, `Summarizing ${pairs} exchanges...`, { parseMode: "plain" });
13281
13355
  }
13282
13356
  const success2 = await summarizeSession(chatId);
13283
13357
  if (success2) {
@@ -13311,14 +13385,14 @@ Tap to toggle:`,
13311
13385
  }
13312
13386
  }
13313
13387
  } else {
13314
- await channel.sendText(chatId, doneText, "plain");
13388
+ await channel.sendText(chatId, doneText, { parseMode: "plain" });
13315
13389
  }
13316
13390
  } else {
13317
13391
  const failText = "Summarization failed. Session log preserved for retry.";
13318
13392
  if (progressMsgId) {
13319
13393
  await channel.editText?.(chatId, progressMsgId, failText, "plain");
13320
13394
  } else {
13321
- await channel.sendText(chatId, failText, "plain");
13395
+ await channel.sendText(chatId, failText, { parseMode: "plain" });
13322
13396
  }
13323
13397
  }
13324
13398
  }
@@ -13383,6 +13457,7 @@ Tap to toggle:`,
13383
13457
  const iQuick = iStats.chat;
13384
13458
  const iDeep = iStats.agentic;
13385
13459
  const intentLine = iTotal > 0 ? `\u26A1 ${iTotal} messages: ${iQuick} quick, ${iDeep} deep` : `\u26A1 No messages classified yet`;
13460
+ const sqCount = getActiveSideQuestCount(chatId);
13386
13461
  const lines = [
13387
13462
  `\u{1F43E} CC-Claw v${VERSION}`,
13388
13463
  `\u23F1 Uptime: ${uptimeStr}`,
@@ -13397,6 +13472,7 @@ Tap to toggle:`,
13397
13472
  `\u{1F4CB} ${sessionId ? sessionId.slice(0, 12) + "..." : "no active session"}`,
13398
13473
  `\u{1F4C1} ${cwd ?? "default workspace"}`,
13399
13474
  `\u{1F4D0} Context: ${ctxBar} ${usedK}K/${maxK}K (${contextPct.toFixed(1)}%)`,
13475
+ ...sqCount > 0 ? [`\u{1F5FA} Side quests: ${sqCount} active`] : [],
13400
13476
  ``,
13401
13477
  buildSectionHeader("Usage"),
13402
13478
  `\u{1F4E8} ${usage2.request_count} requests \xB7 ${(usage2.input_tokens / 1e3).toFixed(0)}K in \xB7 ${(usage2.output_tokens / 1e3).toFixed(0)}K out`,
@@ -13418,7 +13494,7 @@ Tap to toggle:`,
13418
13494
  ]
13419
13495
  ]);
13420
13496
  } else {
13421
- await channel.sendText(chatId, lines.join("\n"), "plain");
13497
+ await channel.sendText(chatId, lines.join("\n"), { parseMode: "plain" });
13422
13498
  }
13423
13499
  break;
13424
13500
  }
@@ -13441,7 +13517,7 @@ Tap to toggle:`,
13441
13517
  const lines = adapters2.map(
13442
13518
  (a) => `${a.id === currentBackend ? "\u2713 " : " "}${a.displayName} (/backend ${a.id})`
13443
13519
  );
13444
- await channel.sendText(chatId, ["Available backends:", "", ...lines].join("\n"), "plain");
13520
+ await channel.sendText(chatId, ["Available backends:", "", ...lines].join("\n"), { parseMode: "plain" });
13445
13521
  }
13446
13522
  break;
13447
13523
  }
@@ -13452,14 +13528,14 @@ Tap to toggle:`,
13452
13528
  if (getAllBackendIds().includes(backendId)) {
13453
13529
  await sendBackendSwitchConfirmation(chatId, backendId, channel);
13454
13530
  } else {
13455
- await channel.sendText(chatId, `Backend "${command}" is not available.`, "plain");
13531
+ await channel.sendText(chatId, `Backend "${command}" is not available.`, { parseMode: "plain" });
13456
13532
  }
13457
13533
  break;
13458
13534
  }
13459
13535
  case "gemini_accounts": {
13460
13536
  const slots = getGeminiSlots();
13461
13537
  if (slots.length === 0) {
13462
- await channel.sendText(chatId, "No Gemini credentials configured.\nAdd with: <code>cc-claw gemini add-key</code> or <code>cc-claw gemini add-account</code>", "html");
13538
+ await channel.sendText(chatId, "No Gemini credentials configured.\nAdd with: <code>cc-claw gemini add-key</code> or <code>cc-claw gemini add-account</code>", { parseMode: "html" });
13463
13539
  break;
13464
13540
  }
13465
13541
  if (typeof channel.sendKeyboard === "function") {
@@ -13493,7 +13569,7 @@ Tap to toggle:`,
13493
13569
  ${list}
13494
13570
 
13495
13571
  Rotation mode: ${currentMode}
13496
- Use: /gemini_accounts <name> to pin`, "plain");
13572
+ Use: /gemini_accounts <name> to pin`, { parseMode: "plain" });
13497
13573
  }
13498
13574
  break;
13499
13575
  }
@@ -13502,7 +13578,7 @@ Use: /gemini_accounts <name> to pin`, "plain");
13502
13578
  if (getAllBackendIds().includes(backendId)) {
13503
13579
  await sendBackendSwitchConfirmation(chatId, backendId, channel);
13504
13580
  } else {
13505
- await channel.sendText(chatId, `Backend "${command}" is not available.`, "plain");
13581
+ await channel.sendText(chatId, `Backend "${command}" is not available.`, { parseMode: "plain" });
13506
13582
  }
13507
13583
  break;
13508
13584
  }
@@ -13511,7 +13587,7 @@ Use: /gemini_accounts <name> to pin`, "plain");
13511
13587
  try {
13512
13588
  adapter = getAdapterForChat(chatId);
13513
13589
  } catch {
13514
- await channel.sendText(chatId, "No backend set. Use /backend first.", "plain");
13590
+ await channel.sendText(chatId, "No backend set. Use /backend first.", { parseMode: "plain" });
13515
13591
  break;
13516
13592
  }
13517
13593
  const models = adapter.availableModels;
@@ -13530,7 +13606,7 @@ Use: /gemini_accounts <name> to pin`, "plain");
13530
13606
  const lines = Object.entries(models).map(
13531
13607
  ([id, info]) => `${id === current ? "\u2713 " : " "}${id} \u2014 ${info.label}`
13532
13608
  );
13533
- await channel.sendText(chatId, [`Models (${adapter.displayName}):`, "", ...lines].join("\n"), "plain");
13609
+ await channel.sendText(chatId, [`Models (${adapter.displayName}):`, "", ...lines].join("\n"), { parseMode: "plain" });
13534
13610
  }
13535
13611
  break;
13536
13612
  }
@@ -13562,7 +13638,7 @@ Use: /gemini_accounts <name> to pin`, "plain");
13562
13638
  } else {
13563
13639
  await channel.sendText(chatId, `Summarizer: ${currentLabel}
13564
13640
 
13565
- Use /summarizer auto, /summarizer off, or /summarizer <backend>:<model>`, "plain");
13641
+ Use /summarizer auto, /summarizer off, or /summarizer <backend>:<model>`, { parseMode: "plain" });
13566
13642
  }
13567
13643
  break;
13568
13644
  }
@@ -13574,24 +13650,24 @@ Use /summarizer auto, /summarizer off, or /summarizer <backend>:<model>`, "plain
13574
13650
  if (parts.length >= 3) {
13575
13651
  const [bk, win, val] = parts;
13576
13652
  if (!getAllBackendIds().includes(bk)) {
13577
- await channel.sendText(chatId, `Unknown backend: ${bk}. Available: ${getAllBackendIds().join(", ")}`, "plain");
13653
+ await channel.sendText(chatId, `Unknown backend: ${bk}. Available: ${getAllBackendIds().join(", ")}`, { parseMode: "plain" });
13578
13654
  break;
13579
13655
  }
13580
13656
  if (!["hourly", "daily", "weekly"].includes(win)) {
13581
- await channel.sendText(chatId, "Window must be: hourly, daily, or weekly", "plain");
13657
+ await channel.sendText(chatId, "Window must be: hourly, daily, or weekly", { parseMode: "plain" });
13582
13658
  break;
13583
13659
  }
13584
13660
  if (val === "off") {
13585
13661
  clearBackendLimit(bk, win);
13586
- await channel.sendText(chatId, `Limit removed for ${bk} (${win}).`, "plain");
13662
+ await channel.sendText(chatId, `Limit removed for ${bk} (${win}).`, { parseMode: "plain" });
13587
13663
  } else {
13588
13664
  const tokens = parseInt(val, 10);
13589
13665
  if (isNaN(tokens) || tokens <= 0) {
13590
- await channel.sendText(chatId, "Token limit must be a positive number.", "plain");
13666
+ await channel.sendText(chatId, "Token limit must be a positive number.", { parseMode: "plain" });
13591
13667
  break;
13592
13668
  }
13593
13669
  setBackendLimit(bk, win, tokens);
13594
- await channel.sendText(chatId, `Limit set: ${bk} (${win}) = ${(tokens / 1e3).toFixed(0)}K input tokens`, "plain");
13670
+ await channel.sendText(chatId, `Limit set: ${bk} (${win}) = ${(tokens / 1e3).toFixed(0)}K input tokens`, { parseMode: "plain" });
13595
13671
  }
13596
13672
  break;
13597
13673
  }
@@ -13609,7 +13685,7 @@ Use /summarizer auto, /summarizer off, or /summarizer <backend>:<model>`, "plain
13609
13685
  current ? `Working directory: ${current}
13610
13686
 
13611
13687
  No saved bookmarks yet. Set a directory with /cwd <path> to auto-save.` : "No working directory set. Usage: /cwd ~/projects/my-app",
13612
- "plain"
13688
+ { parseMode: "plain" }
13613
13689
  );
13614
13690
  return;
13615
13691
  }
@@ -13622,46 +13698,46 @@ Recent directories:` : "Recent directories:";
13622
13698
  } else {
13623
13699
  const list = recents.map((r) => ` ${r.alias} \u2192 ${r.path}`).join("\n");
13624
13700
  await channel.sendText(chatId, `${text}
13625
- ${list}`, "plain");
13701
+ ${list}`, { parseMode: "plain" });
13626
13702
  }
13627
13703
  return;
13628
13704
  }
13629
13705
  if (commandArgs === "reset" || commandArgs === "clear") {
13630
13706
  clearCwd(chatId);
13631
- await channel.sendText(chatId, "Working directory cleared. Using default.", "plain");
13707
+ await channel.sendText(chatId, "Working directory cleared. Using default.", { parseMode: "plain" });
13632
13708
  return;
13633
13709
  }
13634
13710
  if (commandArgs === "aliases") {
13635
13711
  const all = getAllBookmarks(chatId);
13636
13712
  if (all.length === 0) {
13637
- await channel.sendText(chatId, "No bookmarks saved yet.", "plain");
13713
+ await channel.sendText(chatId, "No bookmarks saved yet.", { parseMode: "plain" });
13638
13714
  return;
13639
13715
  }
13640
13716
  const lines = all.map((b) => ` ${b.manual ? "[manual]" : "[auto]"} ${b.alias} \u2192 ${b.path}`);
13641
13717
  await channel.sendText(chatId, `Directory bookmarks:
13642
- ${lines.join("\n")}`, "plain");
13718
+ ${lines.join("\n")}`, { parseMode: "plain" });
13643
13719
  return;
13644
13720
  }
13645
13721
  if (commandArgs.startsWith("unalias ")) {
13646
13722
  const aliasName = commandArgs.slice(8).trim();
13647
13723
  if (!aliasName) {
13648
- await channel.sendText(chatId, "Usage: /cwd unalias <name>", "plain");
13724
+ await channel.sendText(chatId, "Usage: /cwd unalias <name>", { parseMode: "plain" });
13649
13725
  return;
13650
13726
  }
13651
13727
  const deleted = deleteBookmark(chatId, aliasName);
13652
- await channel.sendText(chatId, deleted ? `Bookmark '${aliasName}' removed.` : `Bookmark '${aliasName}' not found.`, "plain");
13728
+ await channel.sendText(chatId, deleted ? `Bookmark '${aliasName}' removed.` : `Bookmark '${aliasName}' not found.`, { parseMode: "plain" });
13653
13729
  return;
13654
13730
  }
13655
13731
  if (commandArgs.startsWith("alias ")) {
13656
13732
  const parts = commandArgs.slice(6).trim().split(/\s+/);
13657
13733
  if (parts.length < 2) {
13658
- await channel.sendText(chatId, "Usage: /cwd alias <name> <path>", "plain");
13734
+ await channel.sendText(chatId, "Usage: /cwd alias <name> <path>", { parseMode: "plain" });
13659
13735
  return;
13660
13736
  }
13661
13737
  const [aliasName, ...pathParts] = parts;
13662
13738
  const aliasPath = pathParts.join(" ").replace(/^~/, process.env.HOME ?? "");
13663
13739
  upsertBookmark(chatId, aliasName, aliasPath, true);
13664
- await channel.sendText(chatId, `Bookmark saved: ${aliasName} \u2192 ${aliasPath}`, "plain");
13740
+ await channel.sendText(chatId, `Bookmark saved: ${aliasName} \u2192 ${aliasPath}`, { parseMode: "plain" });
13665
13741
  return;
13666
13742
  }
13667
13743
  const arg = commandArgs;
@@ -13695,31 +13771,31 @@ ${lines.join("\n")}`, "plain");
13695
13771
  await channel.sendKeyboard(chatId, `Multiple matches for "${arg}":`, buttons);
13696
13772
  return;
13697
13773
  }
13698
- await channel.sendText(chatId, `Directory alias '${arg}' not found. Use /cwd aliases to see saved bookmarks.`, "plain");
13774
+ await channel.sendText(chatId, `Directory alias '${arg}' not found. Use /cwd aliases to see saved bookmarks.`, { parseMode: "plain" });
13699
13775
  break;
13700
13776
  }
13701
13777
  case "memory": {
13702
13778
  if (commandArgs?.startsWith("edit ")) {
13703
13779
  const editMatch = commandArgs.match(/^edit\s+(\d+)\s+(.+)/);
13704
13780
  if (!editMatch) {
13705
- await channel.sendText(chatId, "Usage: /memory edit <id> <new content>", "plain");
13781
+ await channel.sendText(chatId, "Usage: /memory edit <id> <new content>", { parseMode: "plain" });
13706
13782
  break;
13707
13783
  }
13708
13784
  const editId = parseInt(editMatch[1], 10);
13709
13785
  const newContent = editMatch[2];
13710
13786
  const mem = getMemoryById(editId);
13711
13787
  if (!mem) {
13712
- await channel.sendText(chatId, "Memory not found.", "plain");
13788
+ await channel.sendText(chatId, "Memory not found.", { parseMode: "plain" });
13713
13789
  break;
13714
13790
  }
13715
13791
  deleteMemoryById(editId);
13716
13792
  saveMemoryWithEmbedding(mem.trigger, newContent, mem.type);
13717
- await channel.sendText(chatId, `Memory #${editId} updated.`, "plain");
13793
+ await channel.sendText(chatId, `Memory #${editId} updated.`, { parseMode: "plain" });
13718
13794
  break;
13719
13795
  }
13720
13796
  const memories = listMemories();
13721
13797
  if (memories.length === 0) {
13722
- await channel.sendText(chatId, "No memories stored yet.", "plain");
13798
+ await channel.sendText(chatId, "No memories stored yet.", { parseMode: "plain" });
13723
13799
  return;
13724
13800
  }
13725
13801
  if (typeof channel.sendKeyboard === "function") {
@@ -13728,19 +13804,19 @@ ${lines.join("\n")}`, "plain");
13728
13804
  const lines = memories.map(
13729
13805
  (m) => `- ${m.trigger}: ${m.content} (salience: ${m.salience.toFixed(2)})`
13730
13806
  );
13731
- await channel.sendText(chatId, lines.join("\n"), "plain");
13807
+ await channel.sendText(chatId, lines.join("\n"), { parseMode: "plain" });
13732
13808
  }
13733
13809
  break;
13734
13810
  }
13735
13811
  case "remember": {
13736
13812
  if (!commandArgs) {
13737
- await channel.sendText(chatId, "Usage: /remember <text>", "plain");
13813
+ await channel.sendText(chatId, "Usage: /remember <text>", { parseMode: "plain" });
13738
13814
  return;
13739
13815
  }
13740
13816
  const content = commandArgs.replace(/^that\s+/i, "");
13741
13817
  const trigger = content.split(/\s+/).slice(0, 3).join(" ");
13742
13818
  saveMemoryWithEmbedding(trigger, content, "semantic");
13743
- await channel.sendText(chatId, "Got it, I'll remember that.", "plain");
13819
+ await channel.sendText(chatId, "Got it, I'll remember that.", { parseMode: "plain" });
13744
13820
  if (typeof channel.sendKeyboard === "function") {
13745
13821
  await channel.sendKeyboard(chatId, "", [
13746
13822
  [{ label: "\u{1F4DA} View Memories", data: "menu:memory" }]
@@ -13750,14 +13826,14 @@ ${lines.join("\n")}`, "plain");
13750
13826
  }
13751
13827
  case "forget": {
13752
13828
  if (!commandArgs) {
13753
- await channel.sendText(chatId, "Usage: /forget <keyword>", "plain");
13829
+ await channel.sendText(chatId, "Usage: /forget <keyword>", { parseMode: "plain" });
13754
13830
  return;
13755
13831
  }
13756
13832
  const count = forgetMemory(commandArgs);
13757
13833
  await channel.sendText(
13758
13834
  chatId,
13759
13835
  count > 0 ? `Forgot ${count} memory(ies) matching "${commandArgs}".` : `No memories found matching "${commandArgs}".`,
13760
- "plain"
13836
+ { parseMode: "plain" }
13761
13837
  );
13762
13838
  break;
13763
13839
  }
@@ -13772,7 +13848,7 @@ ${lines.join("\n")}`, "plain");
13772
13848
  ]);
13773
13849
  } else {
13774
13850
  const toggled = toggleVoice(chatId);
13775
- await channel.sendText(chatId, toggled ? "Voice responses enabled." : "Voice responses disabled.", "plain");
13851
+ await channel.sendText(chatId, toggled ? "Voice responses enabled." : "Voice responses disabled.", { parseMode: "plain" });
13776
13852
  }
13777
13853
  break;
13778
13854
  }
@@ -13791,7 +13867,7 @@ ${lines.join("\n")}`, "plain");
13791
13867
  ]
13792
13868
  ]);
13793
13869
  } else {
13794
- await channel.sendText(chatId, `Current Response Style: ${currentStyle}`, "plain");
13870
+ await channel.sendText(chatId, `Current Response Style: ${currentStyle}`, { parseMode: "plain" });
13795
13871
  }
13796
13872
  break;
13797
13873
  }
@@ -13808,7 +13884,7 @@ Appends model + thinking level to each response.`, [
13808
13884
  } else {
13809
13885
  const newSig = currentSig === "on" ? "off" : "on";
13810
13886
  setModelSignature(chatId, newSig);
13811
- await channel.sendText(chatId, newSig === "on" ? "Model signature enabled." : "Model signature disabled.", "plain");
13887
+ await channel.sendText(chatId, newSig === "on" ? "Model signature enabled." : "Model signature disabled.", { parseMode: "plain" });
13812
13888
  }
13813
13889
  break;
13814
13890
  }
@@ -13818,7 +13894,7 @@ Appends model + thinking level to each response.`, [
13818
13894
  try {
13819
13895
  adapter = getAdapterForChat(chatId);
13820
13896
  } catch {
13821
- await channel.sendText(chatId, "No backend set. Use /backend first.", "plain");
13897
+ await channel.sendText(chatId, "No backend set. Use /backend first.", { parseMode: "plain" });
13822
13898
  break;
13823
13899
  }
13824
13900
  const currentModel = getModel(chatId) ?? adapter.defaultModel;
@@ -13826,7 +13902,7 @@ Appends model + thinking level to each response.`, [
13826
13902
  const currentLevel = getThinkingLevel(chatId) || "auto";
13827
13903
  if (!modelInfo || modelInfo.thinking !== "adjustable" || !modelInfo.thinkingLevels) {
13828
13904
  await channel.sendText(chatId, `Current model (${shortModelName(currentModel)}) doesn't support adjustable thinking.
13829
- Use /model to pick a model with \u26A1 thinking support.`, "plain");
13905
+ Use /model to pick a model with \u26A1 thinking support.`, { parseMode: "plain" });
13830
13906
  break;
13831
13907
  }
13832
13908
  if (typeof channel.sendKeyboard === "function") {
@@ -13844,31 +13920,31 @@ Current: ${capitalize(currentLevel)}`,
13844
13920
  } else {
13845
13921
  await channel.sendText(chatId, `Thinking: ${capitalize(currentLevel)}
13846
13922
  Levels: ${modelInfo.thinkingLevels.join(", ")}
13847
- Set via callback (keyboard required).`, "plain");
13923
+ Set via callback (keyboard required).`, { parseMode: "plain" });
13848
13924
  }
13849
13925
  break;
13850
13926
  }
13851
13927
  case "imagine":
13852
13928
  case "image": {
13853
13929
  if (!commandArgs) {
13854
- await channel.sendText(chatId, "Usage: /imagine <prompt>\nExample: /imagine a cat astronaut on Mars", "plain");
13930
+ await channel.sendText(chatId, "Usage: /imagine <prompt>\nExample: /imagine a cat astronaut on Mars", { parseMode: "plain" });
13855
13931
  return;
13856
13932
  }
13857
13933
  if (!isImageGenAvailable()) {
13858
- await channel.sendText(chatId, "Image generation requires GEMINI_API_KEY. Configure it in ~/.cc-claw/.env", "plain");
13934
+ await channel.sendText(chatId, "Image generation requires GEMINI_API_KEY. Configure it in ~/.cc-claw/.env", { parseMode: "plain" });
13859
13935
  return;
13860
13936
  }
13861
- await channel.sendText(chatId, "\u{1F3A8} Generating image\u2026", "plain");
13937
+ await channel.sendText(chatId, "\u{1F3A8} Generating image\u2026", { parseMode: "plain" });
13862
13938
  try {
13863
13939
  const result = await generateImage(commandArgs);
13864
13940
  const file = await readFile5(result.filePath);
13865
13941
  const name = result.filePath.split("/").pop() ?? "image.png";
13866
13942
  await channel.sendFile(chatId, file, name);
13867
13943
  if (result.text) {
13868
- await channel.sendText(chatId, result.text, "plain");
13944
+ await channel.sendText(chatId, result.text, { parseMode: "plain" });
13869
13945
  }
13870
13946
  } catch (err) {
13871
- await channel.sendText(chatId, `Image generation failed: ${errorMessage(err)}`, "plain");
13947
+ await channel.sendText(chatId, `Image generation failed: ${errorMessage(err)}`, { parseMode: "plain" });
13872
13948
  }
13873
13949
  break;
13874
13950
  }
@@ -13877,7 +13953,7 @@ Set via callback (keyboard required).`, "plain");
13877
13953
  await channel.sendText(
13878
13954
  chatId,
13879
13955
  "Usage: /schedule <description>\nExample: /schedule every day at 9am summarize AI news\n\nThe wizard will guide you through all configuration options.",
13880
- "plain"
13956
+ { parseMode: "plain" }
13881
13957
  );
13882
13958
  return;
13883
13959
  }
@@ -13898,7 +13974,7 @@ Set via callback (keyboard required).`, "plain");
13898
13974
  await channel.sendText(
13899
13975
  chatId,
13900
13976
  success2 ? `Job #${id} cancelled.` : `Job #${id} not found.`,
13901
- "plain"
13977
+ { parseMode: "plain" }
13902
13978
  );
13903
13979
  break;
13904
13980
  }
@@ -13912,7 +13988,7 @@ Set via callback (keyboard required).`, "plain");
13912
13988
  await channel.sendText(
13913
13989
  chatId,
13914
13990
  paused ? `Job #${pauseId} paused. Use /resume ${pauseId} to re-enable.` : `Job #${pauseId} not found.`,
13915
- "plain"
13991
+ { parseMode: "plain" }
13916
13992
  );
13917
13993
  break;
13918
13994
  }
@@ -13926,7 +14002,7 @@ Set via callback (keyboard required).`, "plain");
13926
14002
  await channel.sendText(
13927
14003
  chatId,
13928
14004
  resumed ? `Job #${resumeId} resumed.` : `Job #${resumeId} not found.`,
13929
- "plain"
14005
+ { parseMode: "plain" }
13930
14006
  );
13931
14007
  break;
13932
14008
  }
@@ -13936,9 +14012,9 @@ Set via callback (keyboard required).`, "plain");
13936
14012
  return;
13937
14013
  }
13938
14014
  const runJobId = parseInt(commandArgs, 10);
13939
- await channel.sendText(chatId, `Triggering job #${runJobId}...`, "plain");
14015
+ await channel.sendText(chatId, `Triggering job #${runJobId}...`, { parseMode: "plain" });
13940
14016
  const result = await triggerJob(runJobId);
13941
- await channel.sendText(chatId, result, "plain");
14017
+ await channel.sendText(chatId, result, { parseMode: "plain" });
13942
14018
  break;
13943
14019
  }
13944
14020
  case "runs": {
@@ -13948,7 +14024,7 @@ Set via callback (keyboard required).`, "plain");
13948
14024
  } else {
13949
14025
  const runs = getJobRuns(void 0, 10);
13950
14026
  if (runs.length === 0) {
13951
- await channel.sendText(chatId, "No run history yet.", "plain");
14027
+ await channel.sendText(chatId, "No run history yet.", { parseMode: "plain" });
13952
14028
  return;
13953
14029
  }
13954
14030
  const lines = runs.map((r) => {
@@ -13959,13 +14035,13 @@ Set via callback (keyboard required).`, "plain");
13959
14035
  Tokens: ${r.usageInput}in / ${r.usageOutput}out` : "";
13960
14036
  return `#${r.jobId} [${r.status}] ${formatLocalDateTime(r.startedAt)}${duration}${error3}${usage2}`;
13961
14037
  });
13962
- await channel.sendText(chatId, lines.join("\n\n"), "plain");
14038
+ await channel.sendText(chatId, lines.join("\n\n"), { parseMode: "plain" });
13963
14039
  }
13964
14040
  break;
13965
14041
  }
13966
14042
  case "editjob": {
13967
14043
  if (!commandArgs) {
13968
- await channel.sendText(chatId, "Usage: /editjob <job-id>", "plain");
14044
+ await channel.sendText(chatId, "Usage: /editjob <job-id>", { parseMode: "plain" });
13969
14045
  return;
13970
14046
  }
13971
14047
  const editId = parseInt(commandArgs, 10);
@@ -13974,7 +14050,7 @@ Set via callback (keyboard required).`, "plain");
13974
14050
  }
13975
14051
  case "health": {
13976
14052
  const report = getHealthReport();
13977
- await channel.sendText(chatId, formatHealthReport(report), "plain");
14053
+ await channel.sendText(chatId, formatHealthReport(report), { parseMode: "plain" });
13978
14054
  if (typeof channel.sendKeyboard === "function") {
13979
14055
  await channel.sendKeyboard(chatId, "", [
13980
14056
  [
@@ -13990,7 +14066,7 @@ Set via callback (keyboard required).`, "plain");
13990
14066
  if (query) {
13991
14067
  const results = searchMessageLog(chatId, query, 20);
13992
14068
  if (results.length === 0) {
13993
- await channel.sendText(chatId, `No matching history found. Try /memories ${query} for older sessions.`, "plain");
14069
+ await channel.sendText(chatId, `No matching history found. Try /memories ${query} for older sessions.`, { parseMode: "plain" });
13994
14070
  break;
13995
14071
  }
13996
14072
  const lines = [`\u{1F4CB} History: "${query}"`, "\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501"];
@@ -14001,14 +14077,14 @@ Set via callback (keyboard required).`, "plain");
14001
14077
  lines.push(`[${row.backend ?? "?"} \xB7 ${date}] ${roleLabel}: ${preview}`);
14002
14078
  }
14003
14079
  lines.push("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501");
14004
- await channel.sendText(chatId, lines.join("\n"), "plain");
14080
+ await channel.sendText(chatId, lines.join("\n"), { parseMode: "plain" });
14005
14081
  } else {
14006
14082
  if (typeof channel.sendKeyboard === "function") {
14007
14083
  await sendHistoryView(chatId, channel, {});
14008
14084
  } else {
14009
14085
  const rows = getRecentMessageLog(chatId, 20).reverse();
14010
14086
  if (rows.length === 0) {
14011
- await channel.sendText(chatId, "No conversation history found.", "plain");
14087
+ await channel.sendText(chatId, "No conversation history found.", { parseMode: "plain" });
14012
14088
  break;
14013
14089
  }
14014
14090
  const lines = ["\u{1F4CB} Recent conversation (last 10 exchanges):", "\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501"];
@@ -14019,7 +14095,7 @@ Set via callback (keyboard required).`, "plain");
14019
14095
  lines.push(`[${row.backend ?? "?"} \xB7 ${date}] ${roleLabel}: ${preview}`);
14020
14096
  }
14021
14097
  lines.push("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501");
14022
- await channel.sendText(chatId, lines.join("\n"), "plain");
14098
+ await channel.sendText(chatId, lines.join("\n"), { parseMode: "plain" });
14023
14099
  }
14024
14100
  }
14025
14101
  break;
@@ -14027,7 +14103,7 @@ Set via callback (keyboard required).`, "plain");
14027
14103
  case "skills": {
14028
14104
  const skills2 = await discoverAllSkills();
14029
14105
  if (skills2.length === 0) {
14030
- await channel.sendText(chatId, "No skills found. Install skills with /skill-install <github-url> or place them in ~/.cc-claw/workspace/skills/", "plain");
14106
+ await channel.sendText(chatId, "No skills found. Install skills with /skill-install <github-url> or place them in ~/.cc-claw/workspace/skills/", { parseMode: "plain" });
14031
14107
  return;
14032
14108
  }
14033
14109
  const page = commandArgs ? parseInt(commandArgs, 10) || 1 : 1;
@@ -14036,16 +14112,16 @@ Set via callback (keyboard required).`, "plain");
14036
14112
  }
14037
14113
  case "skill-install": {
14038
14114
  if (!commandArgs) {
14039
- await channel.sendText(chatId, "Usage: /skill-install <github-url>\nExample: /skill-install jacob-bd/universal-skills-manager", "plain");
14115
+ await channel.sendText(chatId, "Usage: /skill-install <github-url>\nExample: /skill-install jacob-bd/universal-skills-manager", { parseMode: "plain" });
14040
14116
  return;
14041
14117
  }
14042
- await channel.sendText(chatId, `Installing skill from ${commandArgs}...`, "plain");
14118
+ await channel.sendText(chatId, `Installing skill from ${commandArgs}...`, { parseMode: "plain" });
14043
14119
  const result = await installSkillFromGitHub(commandArgs);
14044
14120
  if (result.success) {
14045
14121
  await channel.sendText(chatId, `Skill "${result.skillName}" installed to ~/.cc-claw/workspace/skills/
14046
- Use /skills to see it.`, "plain");
14122
+ Use /skills to see it.`, { parseMode: "plain" });
14047
14123
  } else {
14048
- await channel.sendText(chatId, `Installation failed: ${result.error}`, "plain");
14124
+ await channel.sendText(chatId, `Installation failed: ${result.error}`, { parseMode: "plain" });
14049
14125
  }
14050
14126
  break;
14051
14127
  }
@@ -14079,10 +14155,10 @@ Use /skills to see it.`, "plain");
14079
14155
  }
14080
14156
  } else {
14081
14157
  if (aliases.length === 0) {
14082
- await channel.sendText(chatId, "No chat aliases configured yet.\nUsage: /chats name <chat_id> <alias>\n\nGroup chats are auto-discovered when the bot receives a message from them.", "plain");
14158
+ await channel.sendText(chatId, "No chat aliases configured yet.\nUsage: /chats name <chat_id> <alias>\n\nGroup chats are auto-discovered when the bot receives a message from them.", { parseMode: "plain" });
14083
14159
  } else {
14084
14160
  const lines = ["Authorized chats:", "", ...aliases.map((a) => ` ${a.alias} -> ${a.chatId}`)];
14085
- await channel.sendText(chatId, lines.join("\n"), "plain");
14161
+ await channel.sendText(chatId, lines.join("\n"), { parseMode: "plain" });
14086
14162
  }
14087
14163
  }
14088
14164
  break;
@@ -14092,12 +14168,12 @@ Use /skills to see it.`, "plain");
14092
14168
  const targetChatId = parts[1];
14093
14169
  const alias = parts.slice(2).join("-").toLowerCase();
14094
14170
  setChatAlias(alias, targetChatId);
14095
- await channel.sendText(chatId, `Alias set: ${alias} -> ${targetChatId}`, "plain");
14171
+ await channel.sendText(chatId, `Alias set: ${alias} -> ${targetChatId}`, { parseMode: "plain" });
14096
14172
  } else if (parts[0] === "remove" && parts[1]) {
14097
14173
  const removed = removeChatAlias(parts[1]);
14098
- await channel.sendText(chatId, removed ? `Alias "${parts[1]}" removed.` : `Alias "${parts[1]}" not found.`, "plain");
14174
+ await channel.sendText(chatId, removed ? `Alias "${parts[1]}" removed.` : `Alias "${parts[1]}" not found.`, { parseMode: "plain" });
14099
14175
  } else {
14100
- await channel.sendText(chatId, "Usage:\n /chats \u2014 list all aliases\n /chats name <chat_id> <alias> \u2014 assign alias\n /chats remove <alias> \u2014 remove alias", "plain");
14176
+ await channel.sendText(chatId, "Usage:\n /chats \u2014 list all aliases\n /chats name <chat_id> <alias> \u2014 assign alias\n /chats remove <alias> \u2014 remove alias", { parseMode: "plain" });
14101
14177
  }
14102
14178
  break;
14103
14179
  }
@@ -14111,7 +14187,7 @@ Use /skills to see it.`, "plain");
14111
14187
  if (typeof channel.sendKeyboard === "function") {
14112
14188
  await sendHeartbeatKeyboard(chatId, channel);
14113
14189
  } else {
14114
- await channel.sendText(chatId, "Heartbeat enabled. I'll check in periodically.", "plain");
14190
+ await channel.sendText(chatId, "Heartbeat enabled. I'll check in periodically.", { parseMode: "plain" });
14115
14191
  }
14116
14192
  break;
14117
14193
  }
@@ -14121,14 +14197,14 @@ Use /skills to see it.`, "plain");
14121
14197
  if (typeof channel.sendKeyboard === "function") {
14122
14198
  await sendHeartbeatKeyboard(chatId, channel);
14123
14199
  } else {
14124
- await channel.sendText(chatId, "Heartbeat disabled.", "plain");
14200
+ await channel.sendText(chatId, "Heartbeat disabled.", { parseMode: "plain" });
14125
14201
  }
14126
14202
  break;
14127
14203
  }
14128
14204
  case "interval": {
14129
14205
  const ms = parseIntervalToMs(value ?? "30m");
14130
14206
  if (!ms || ms < 6e4) {
14131
- await channel.sendText(chatId, "Invalid interval. Use e.g. 15m, 30m, 1h, 2h", "plain");
14207
+ await channel.sendText(chatId, "Invalid interval. Use e.g. 15m, 30m, 1h, 2h", { parseMode: "plain" });
14132
14208
  break;
14133
14209
  }
14134
14210
  setHeartbeatConfig(chatId, { intervalMs: ms });
@@ -14138,14 +14214,14 @@ Use /skills to see it.`, "plain");
14138
14214
  if (typeof channel.sendKeyboard === "function") {
14139
14215
  await sendHeartbeatKeyboard(chatId, channel);
14140
14216
  } else {
14141
- await channel.sendText(chatId, `Heartbeat interval set to ${ms / 6e4} minutes.`, "plain");
14217
+ await channel.sendText(chatId, `Heartbeat interval set to ${ms / 6e4} minutes.`, { parseMode: "plain" });
14142
14218
  }
14143
14219
  break;
14144
14220
  }
14145
14221
  case "hours": {
14146
14222
  const hourMatch = (value ?? "").match(/^(\d{1,2})-(\d{1,2})$/);
14147
14223
  if (!hourMatch) {
14148
- await channel.sendText(chatId, "Usage: /heartbeat hours 8-22", "plain");
14224
+ await channel.sendText(chatId, "Usage: /heartbeat hours 8-22", { parseMode: "plain" });
14149
14225
  break;
14150
14226
  }
14151
14227
  const hbStart = `${hourMatch[1].padStart(2, "0")}:00`;
@@ -14154,7 +14230,7 @@ Use /skills to see it.`, "plain");
14154
14230
  if (typeof channel.sendKeyboard === "function") {
14155
14231
  await sendHeartbeatKeyboard(chatId, channel);
14156
14232
  } else {
14157
- await channel.sendText(chatId, `Active hours: ${hbStart} - ${hbEnd}`, "plain");
14233
+ await channel.sendText(chatId, `Active hours: ${hbStart} - ${hbEnd}`, { parseMode: "plain" });
14158
14234
  }
14159
14235
  break;
14160
14236
  }
@@ -14162,7 +14238,7 @@ Use /skills to see it.`, "plain");
14162
14238
  if (typeof channel.sendKeyboard === "function") {
14163
14239
  await sendHeartbeatKeyboard(chatId, channel);
14164
14240
  } else {
14165
- await channel.sendText(chatId, formatHeartbeatStatus(chatId), "plain");
14241
+ await channel.sendText(chatId, formatHeartbeatStatus(chatId), { parseMode: "plain" });
14166
14242
  }
14167
14243
  }
14168
14244
  break;
@@ -14170,7 +14246,7 @@ Use /skills to see it.`, "plain");
14170
14246
  if (typeof channel.sendKeyboard === "function") {
14171
14247
  await sendHeartbeatKeyboard(chatId, channel);
14172
14248
  } else {
14173
- await channel.sendText(chatId, formatHeartbeatStatus(chatId), "plain");
14249
+ await channel.sendText(chatId, formatHeartbeatStatus(chatId), { parseMode: "plain" });
14174
14250
  }
14175
14251
  break;
14176
14252
  }
@@ -14185,7 +14261,7 @@ Use /skills to see it.`, "plain");
14185
14261
  } catch {
14186
14262
  }
14187
14263
  clearSession(chatId);
14188
- await channel.sendText(chatId, `Agent mode set to <b>${modeArg}</b>. Session cleared.`, "html");
14264
+ await channel.sendText(chatId, `Agent mode set to <b>${modeArg}</b>. Session cleared.`, { parseMode: "html" });
14189
14265
  } else if (typeof channel.sendKeyboard === "function") {
14190
14266
  const current = getAgentMode(chatId);
14191
14267
  await channel.sendKeyboard(chatId, `Agent mode: <b>${current}</b>
@@ -14199,7 +14275,7 @@ Choose a mode:`, [
14199
14275
  ]);
14200
14276
  } else {
14201
14277
  const current = getAgentMode(chatId);
14202
- await channel.sendText(chatId, `Agent mode: ${current}. Use /agents mode <auto|native|claw> to change.`, "plain");
14278
+ await channel.sendText(chatId, `Agent mode: ${current}. Use /agents mode <auto|native|claw> to change.`, { parseMode: "plain" });
14203
14279
  }
14204
14280
  return;
14205
14281
  }
@@ -14212,7 +14288,7 @@ Choose a mode:`, [
14212
14288
  ORDER BY createdAt DESC LIMIT 20
14213
14289
  `).all(chatId);
14214
14290
  if (events.length === 0) {
14215
- await channel.sendText(chatId, "No native sub-agent activity in the last 24h.", "plain");
14291
+ await channel.sendText(chatId, "No native sub-agent activity in the last 24h.", { parseMode: "plain" });
14216
14292
  } else {
14217
14293
  const lines2 = events.map((e) => {
14218
14294
  const d = e.detail ? JSON.parse(e.detail) : {};
@@ -14220,7 +14296,7 @@ Choose a mode:`, [
14220
14296
  });
14221
14297
  await channel.sendText(chatId, `<b>Native agent history (24h)</b>
14222
14298
 
14223
- ${lines2.join("\n")}`, "html");
14299
+ ${lines2.join("\n")}`, { parseMode: "html" });
14224
14300
  }
14225
14301
  return;
14226
14302
  }
@@ -14237,7 +14313,7 @@ ${lines2.join("\n")}`, "html");
14237
14313
  const runners2 = getAllRunners();
14238
14314
  const runnerMap = new Map(runners2.map((r) => [r.id, r]));
14239
14315
  if (agents2.length === 0) {
14240
- await channel.sendText(chatId, "<b>Active Agents</b>\n\nNo active agents.", "html");
14316
+ await channel.sendText(chatId, "<b>Active Agents</b>\n\nNo active agents.", { parseMode: "html" });
14241
14317
  break;
14242
14318
  }
14243
14319
  const { getAllAdapters: getAllAdapters3 } = await Promise.resolve().then(() => (init_backends(), backends_exports));
@@ -14271,7 +14347,7 @@ ${lines2.join("\n")}`, "html");
14271
14347
  lines.push("");
14272
14348
  }
14273
14349
  lines.push(`Total: ${agents2.length} agent(s) (${runningCount} running, ${queuedCount} queued)`);
14274
- await channel.sendText(chatId, lines.join("\n"), "html");
14350
+ await channel.sendText(chatId, lines.join("\n"), { parseMode: "html" });
14275
14351
  if (typeof channel.sendKeyboard === "function") {
14276
14352
  const agentButtons = [];
14277
14353
  for (const a of agents2) {
@@ -14290,7 +14366,7 @@ ${lines2.join("\n")}`, "html");
14290
14366
  const db3 = getDb();
14291
14367
  const orch = getActiveOrchestration(db3, chatId);
14292
14368
  if (!orch) {
14293
- await channel.sendText(chatId, "<b>Task Board</b>\n\nNo active orchestration. Start a task to create one.", "html");
14369
+ await channel.sendText(chatId, "<b>Task Board</b>\n\nNo active orchestration. Start a task to create one.", { parseMode: "html" });
14294
14370
  break;
14295
14371
  }
14296
14372
  const tasks = listTasksByOrchestration(db3, orch.id);
@@ -14325,7 +14401,7 @@ ${lines2.join("\n")}`, "html");
14325
14401
  if (lines.length === 2) {
14326
14402
  lines.push("No tasks yet.");
14327
14403
  }
14328
- await channel.sendText(chatId, lines.join("\n").trimEnd(), "html");
14404
+ await channel.sendText(chatId, lines.join("\n").trimEnd(), { parseMode: "html" });
14329
14405
  if (typeof channel.sendKeyboard === "function" && tasks.length > 0) {
14330
14406
  const taskButtons = [];
14331
14407
  const viewable = tasks.filter((t) => t.status === "pending" || t.status === "in_progress");
@@ -14346,7 +14422,7 @@ ${lines2.join("\n")}`, "html");
14346
14422
  const db4 = getDb();
14347
14423
  const agents3 = listActiveAgents(db4);
14348
14424
  if (agents3.length === 0) {
14349
- await channel.sendText(chatId, "No active agents to stop.", "plain");
14425
+ await channel.sendText(chatId, "No active agents to stop.", { parseMode: "plain" });
14350
14426
  break;
14351
14427
  }
14352
14428
  if (typeof channel.sendKeyboard === "function") {
@@ -14374,7 +14450,7 @@ ${lines2.join("\n")}`, "html");
14374
14450
 
14375
14451
  ${agentLines.join("\n")}`, buttons);
14376
14452
  } else {
14377
- await channel.sendText(chatId, "Usage: /stopagent <id>\nUse first 8 chars of agent ID or full ID.", "plain");
14453
+ await channel.sendText(chatId, "Usage: /stopagent <id>\nUse first 8 chars of agent ID or full ID.", { parseMode: "plain" });
14378
14454
  }
14379
14455
  break;
14380
14456
  }
@@ -14383,16 +14459,16 @@ ${agentLines.join("\n")}`, buttons);
14383
14459
  const id = commandArgs.trim();
14384
14460
  const match = agents2.find((a) => a.id === id || a.id.startsWith(id));
14385
14461
  if (!match) {
14386
- await channel.sendText(chatId, `No active agent found matching "${id}". Use /agents to list.`, "plain");
14462
+ await channel.sendText(chatId, `No active agent found matching "${id}". Use /agents to list.`, { parseMode: "plain" });
14387
14463
  break;
14388
14464
  }
14389
14465
  const ok = cancelAgent(match.id);
14390
- await channel.sendText(chatId, ok ? `Agent ${match.id.slice(0, 8)} cancelled.` : "Could not cancel agent.", "plain");
14466
+ await channel.sendText(chatId, ok ? `Agent ${match.id.slice(0, 8)} cancelled.` : "Could not cancel agent.", { parseMode: "plain" });
14391
14467
  break;
14392
14468
  }
14393
14469
  case "stopall": {
14394
14470
  const count = cancelAllAgents(chatId);
14395
- await channel.sendText(chatId, count > 0 ? `Cancelled ${count} agent(s).` : "No active agents to cancel.", "plain");
14471
+ await channel.sendText(chatId, count > 0 ? `Cancelled ${count} agent(s).` : "No active agents to cancel.", { parseMode: "plain" });
14396
14472
  if (count > 0 && typeof channel.sendKeyboard === "function") {
14397
14473
  await channel.sendKeyboard(chatId, "", [
14398
14474
  [
@@ -14406,7 +14482,7 @@ ${agentLines.join("\n")}`, buttons);
14406
14482
  case "runners": {
14407
14483
  const runners2 = getAllRunners();
14408
14484
  if (runners2.length === 0) {
14409
- await channel.sendText(chatId, "<b>Registered Runners</b>\n\nNo runners registered.", "html");
14485
+ await channel.sendText(chatId, "<b>Registered Runners</b>\n\nNo runners registered.", { parseMode: "html" });
14410
14486
  break;
14411
14487
  }
14412
14488
  const lines = ["<b>Registered Runners</b>", ""];
@@ -14414,7 +14490,7 @@ ${agentLines.join("\n")}`, buttons);
14414
14490
  const specialties = r.capabilities.specialties?.join(", ") ?? "";
14415
14491
  lines.push(`\u2022 <b>${r.id}</b> (${r.displayName}) \u2014 ${specialties}`);
14416
14492
  }
14417
- await channel.sendText(chatId, lines.join("\n"), "html");
14493
+ await channel.sendText(chatId, lines.join("\n"), { parseMode: "html" });
14418
14494
  break;
14419
14495
  }
14420
14496
  case "mcp":
@@ -14483,7 +14559,7 @@ ${agentLines.join("\n")}`, buttons);
14483
14559
  } else {
14484
14560
  lines.splice(1, 0, `<i>${connectedCount}/${totalCount} connected</i>`);
14485
14561
  }
14486
- await channel.sendText(chatId, lines.join("\n"), "html");
14562
+ await channel.sendText(chatId, lines.join("\n"), { parseMode: "html" });
14487
14563
  break;
14488
14564
  }
14489
14565
  case "cron": {
@@ -14502,7 +14578,7 @@ ${agentLines.join("\n")}`, buttons);
14502
14578
  }
14503
14579
  const id = parseInt(cronSubArgs, 10);
14504
14580
  const ok = cancelJob(id);
14505
- await channel.sendText(chatId, ok ? `Job #${id} cancelled.` : `Job #${id} not found.`, "plain");
14581
+ await channel.sendText(chatId, ok ? `Job #${id} cancelled.` : `Job #${id} not found.`, { parseMode: "plain" });
14506
14582
  break;
14507
14583
  }
14508
14584
  case "pause": {
@@ -14512,7 +14588,7 @@ ${agentLines.join("\n")}`, buttons);
14512
14588
  }
14513
14589
  const pauseId = parseInt(cronSubArgs, 10);
14514
14590
  const paused = pauseJob(pauseId);
14515
- await channel.sendText(chatId, paused ? `Job #${pauseId} paused.` : `Job #${pauseId} not found.`, "plain");
14591
+ await channel.sendText(chatId, paused ? `Job #${pauseId} paused.` : `Job #${pauseId} not found.`, { parseMode: "plain" });
14516
14592
  break;
14517
14593
  }
14518
14594
  case "resume": {
@@ -14522,7 +14598,7 @@ ${agentLines.join("\n")}`, buttons);
14522
14598
  }
14523
14599
  const resumeId = parseInt(cronSubArgs, 10);
14524
14600
  const resumed = resumeJob(resumeId);
14525
- await channel.sendText(chatId, resumed ? `Job #${resumeId} resumed.` : `Job #${resumeId} not found.`, "plain");
14601
+ await channel.sendText(chatId, resumed ? `Job #${resumeId} resumed.` : `Job #${resumeId} not found.`, { parseMode: "plain" });
14526
14602
  break;
14527
14603
  }
14528
14604
  case "run": {
@@ -14531,9 +14607,9 @@ ${agentLines.join("\n")}`, buttons);
14531
14607
  return;
14532
14608
  }
14533
14609
  const runId = parseInt(cronSubArgs, 10);
14534
- await channel.sendText(chatId, `Triggering job #${runId}...`, "plain");
14610
+ await channel.sendText(chatId, `Triggering job #${runId}...`, { parseMode: "plain" });
14535
14611
  const runResult = await triggerJob(runId);
14536
- await channel.sendText(chatId, runResult, "plain");
14612
+ await channel.sendText(chatId, runResult, { parseMode: "plain" });
14537
14613
  break;
14538
14614
  }
14539
14615
  case "runs": {
@@ -14543,20 +14619,20 @@ ${agentLines.join("\n")}`, buttons);
14543
14619
  } else {
14544
14620
  const cronRuns2 = getJobRuns(void 0, 10);
14545
14621
  if (cronRuns2.length === 0) {
14546
- await channel.sendText(chatId, "No run history.", "plain");
14622
+ await channel.sendText(chatId, "No run history.", { parseMode: "plain" });
14547
14623
  return;
14548
14624
  }
14549
14625
  const runLines = cronRuns2.map((r) => {
14550
14626
  const dur = r.durationMs ? ` (${(r.durationMs / 1e3).toFixed(1)}s)` : "";
14551
14627
  return `#${r.jobId} [${r.status}] ${formatLocalDateTime(r.startedAt)}${dur}`;
14552
14628
  });
14553
- await channel.sendText(chatId, runLines.join("\n\n"), "plain");
14629
+ await channel.sendText(chatId, runLines.join("\n\n"), { parseMode: "plain" });
14554
14630
  }
14555
14631
  break;
14556
14632
  }
14557
14633
  case "edit": {
14558
14634
  if (!cronSubArgs) {
14559
- await channel.sendText(chatId, "Usage: /cron edit <id>", "plain");
14635
+ await channel.sendText(chatId, "Usage: /cron edit <id>", { parseMode: "plain" });
14560
14636
  return;
14561
14637
  }
14562
14638
  const editId = parseInt(cronSubArgs, 10);
@@ -14565,7 +14641,7 @@ ${agentLines.join("\n")}`, buttons);
14565
14641
  }
14566
14642
  case "health": {
14567
14643
  const report = getHealthReport();
14568
- await channel.sendText(chatId, formatHealthReport(report), "plain");
14644
+ await channel.sendText(chatId, formatHealthReport(report), { parseMode: "plain" });
14569
14645
  break;
14570
14646
  }
14571
14647
  default:
@@ -14577,84 +14653,112 @@ ${agentLines.join("\n")}`, buttons);
14577
14653
  const testMsg = commandArgs?.trim() || "hey";
14578
14654
  const result = classifyIntent(testMsg, chatId);
14579
14655
  await channel.sendText(chatId, `Intent: ${result}
14580
- Message: "${testMsg}"`, "plain");
14656
+ Message: "${testMsg}"`, { parseMode: "plain" });
14581
14657
  break;
14582
14658
  }
14583
14659
  case "evolve": {
14584
- const { buildEvolveMenuKeyboard: buildEvolveMenuKeyboard2 } = await Promise.resolve().then(() => (init_propose(), propose_exports));
14585
- const keyboard = buildEvolveMenuKeyboard2();
14586
- await channel.sendKeyboard(chatId, "Self-Learning & Evolution", keyboard);
14587
- break;
14588
- }
14589
- case "reflect": {
14590
- if (typeof channel.sendKeyboard === "function") {
14591
- const { getUnprocessedSignalCount: getUnprocessedSignalCount2, getLastAnalysisTime: getLastAnalysisTime2 } = await Promise.resolve().then(() => (init_store4(), store_exports4));
14592
- const signalCount = getUnprocessedSignalCount2(getDb(), chatId);
14660
+ const { getReflectionStatus: getRefStatus, getUnprocessedSignalCount: getUnprocessedSignalCount2, getPendingInsightCount: getPendingInsightCount2, getLastAnalysisTime: getLastAnalysisTime2 } = await Promise.resolve().then(() => (init_store4(), store_exports4));
14661
+ const isActive = getRefStatus(getDb(), chatId) === "active";
14662
+ if (!isActive) {
14663
+ const text = [
14664
+ "Self-Learning & Evolution",
14665
+ "\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501",
14666
+ "",
14667
+ "Teach your assistant to improve over time.",
14668
+ "",
14669
+ "When enabled, CC-Claw watches for corrections,",
14670
+ "preferences, and frustration in your messages,",
14671
+ "then proposes changes to its personality and",
14672
+ "behavior files (SOUL.md, USER.md).",
14673
+ "",
14674
+ "You review and approve every change."
14675
+ ].join("\n");
14676
+ if (typeof channel.sendKeyboard === "function") {
14677
+ const { buildEvolveOnboardingKeyboard: buildEvolveOnboardingKeyboard2 } = await Promise.resolve().then(() => (init_propose(), propose_exports));
14678
+ await channel.sendKeyboard(chatId, text, buildEvolveOnboardingKeyboard2());
14679
+ } else {
14680
+ await channel.sendText(chatId, text + "\n\nUse /evolve enable to activate.", { parseMode: "plain" });
14681
+ }
14682
+ } else {
14683
+ const signals = getUnprocessedSignalCount2(getDb(), chatId);
14684
+ const pending = getPendingInsightCount2(getDb(), chatId);
14593
14685
  const lastTime = getLastAnalysisTime2(getDb(), chatId);
14594
- let lastAnalysisText = "never";
14686
+ let lastText = "never";
14595
14687
  if (lastTime) {
14596
14688
  const diffMs = Date.now() - (/* @__PURE__ */ new Date(lastTime + "Z")).getTime();
14597
14689
  const diffHours = Math.floor(diffMs / 36e5);
14598
- if (diffHours < 1) lastAnalysisText = "less than an hour ago";
14599
- else if (diffHours < 24) lastAnalysisText = `${diffHours} hour${diffHours === 1 ? "" : "s"} ago`;
14600
- else {
14601
- const diffDays = Math.floor(diffHours / 24);
14602
- lastAnalysisText = `${diffDays} day${diffDays === 1 ? "" : "s"} ago`;
14603
- }
14690
+ if (diffHours < 1) lastText = "< 1 hour ago";
14691
+ else if (diffHours < 24) lastText = `${diffHours}h ago`;
14692
+ else lastText = `${Math.floor(diffHours / 24)}d ago`;
14604
14693
  }
14605
- const text = [
14606
- "Reflection Analysis",
14694
+ const dashText = [
14695
+ "Self-Learning & Evolution",
14607
14696
  "\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501",
14608
14697
  "",
14609
- "This will analyze recent interactions for",
14610
- "improvement insights. Uses the summarizer model.",
14611
- "",
14612
- `Unprocessed signals: ${signalCount}`,
14613
- `Last analysis: ${lastAnalysisText}`
14698
+ `\u2705 Active`,
14699
+ `Signals: ${signals} pending \xB7 Proposals: ${pending}`,
14700
+ `Last analysis: ${lastText}`
14614
14701
  ].join("\n");
14615
- await channel.sendKeyboard(chatId, text, [[
14616
- { label: "Analyze Now", data: "reflect:go", style: "success" },
14617
- { label: "Cancel", data: "reflect:cancel" }
14618
- ]]);
14619
- } else {
14620
- const { runAnalysis: runAnalysis2 } = await Promise.resolve().then(() => (init_analyze(), analyze_exports));
14621
- const { formatNightlySummary: formatNightlySummary2 } = await Promise.resolve().then(() => (init_propose(), propose_exports));
14622
- await channel.sendText(chatId, "Analyzing recent interactions...", "plain");
14623
- try {
14624
- const insights = await runAnalysis2(chatId);
14625
- if (insights.length === 0) {
14626
- await channel.sendText(chatId, "No new insights from recent interactions.", "plain");
14702
+ if (typeof channel.sendKeyboard === "function") {
14703
+ const { buildEvolveMenuKeyboard: buildEvolveMenuKeyboard2 } = await Promise.resolve().then(() => (init_propose(), propose_exports));
14704
+ await channel.sendKeyboard(chatId, dashText, buildEvolveMenuKeyboard2({ pendingProposals: pending, unprocessedSignals: signals }));
14705
+ } else {
14706
+ await channel.sendText(chatId, dashText, { parseMode: "plain" });
14707
+ }
14708
+ }
14709
+ break;
14710
+ }
14711
+ case "reflect": {
14712
+ const { runAnalysis: runAnalysis2 } = await Promise.resolve().then(() => (init_analyze(), analyze_exports));
14713
+ const { formatNightlySummary: formatNightlySummary2 } = await Promise.resolve().then(() => (init_propose(), propose_exports));
14714
+ await channel.sendText(chatId, "Analyzing recent interactions...", { parseMode: "plain" });
14715
+ try {
14716
+ const insights = await runAnalysis2(chatId, { force: true });
14717
+ if (insights.length === 0) {
14718
+ const { getReflectionStatus: getRefStatus } = await Promise.resolve().then(() => (init_store4(), store_exports4));
14719
+ const isFrozen = getRefStatus(getDb(), chatId) === "frozen";
14720
+ if (isFrozen) {
14721
+ const msg2 = "No insights found. Self-learning is disabled \u2014\nenable it with /evolve so feedback signals are\ncaptured automatically between analyses.";
14722
+ if (typeof channel.sendKeyboard === "function") {
14723
+ await channel.sendKeyboard(chatId, msg2, [[
14724
+ { label: "Open /evolve", data: "evolve:menu", style: "primary" }
14725
+ ]]);
14726
+ } else {
14727
+ await channel.sendText(chatId, msg2, { parseMode: "plain" });
14728
+ }
14627
14729
  } else {
14628
- const items = insights.map((ins, i) => ({ id: i + 1, category: ins.category, insight: ins.insight }));
14629
- await channel.sendText(chatId, formatNightlySummary2(items), "plain");
14730
+ await channel.sendText(chatId, "No new insights from recent interactions.", { parseMode: "plain" });
14630
14731
  }
14631
- } catch (e) {
14632
- await channel.sendText(chatId, `Analysis failed: ${e}`, "plain");
14732
+ } else {
14733
+ const items = insights.map((ins, i) => ({ id: i + 1, category: ins.category, insight: ins.insight }));
14734
+ await channel.sendText(chatId, formatNightlySummary2(items) + "\n\nUse /evolve to review and apply proposals.", { parseMode: "plain" });
14633
14735
  }
14736
+ } catch (e) {
14737
+ await channel.sendText(chatId, `Analysis failed: ${e}`, { parseMode: "plain" });
14634
14738
  }
14635
14739
  break;
14636
14740
  }
14637
14741
  default:
14638
- await channel.sendText(chatId, `Unknown command: /${command}. Type /help for available commands.`, "plain");
14742
+ await channel.sendText(chatId, `Unknown command: /${command}. Type /help for available commands.`, { parseMode: "plain" });
14639
14743
  }
14640
14744
  }
14641
14745
  async function handleVoice(msg, channel) {
14642
14746
  const { chatId, fileName } = msg;
14643
- await channel.sendText(chatId, "Transcribing voice message...", "plain");
14747
+ await channel.sendText(chatId, "Transcribing voice message...", { parseMode: "plain" });
14644
14748
  try {
14645
14749
  const audioBuffer = await channel.downloadFile(fileName);
14646
14750
  const transcript = await transcribeAudio(audioBuffer);
14647
14751
  if (!transcript) {
14648
- await channel.sendText(chatId, "Couldn't transcribe the voice message.", "plain");
14752
+ await channel.sendText(chatId, "Couldn't transcribe the voice message.", { parseMode: "plain" });
14649
14753
  return;
14650
14754
  }
14651
14755
  const vBackendId = getBackend(chatId) ?? "claude";
14652
14756
  const vLimitMsg = checkBackendLimits(vBackendId);
14653
14757
  if (vLimitMsg) {
14654
- await channel.sendText(chatId, vLimitMsg, "plain");
14758
+ await channel.sendText(chatId, vLimitMsg, { parseMode: "plain" });
14655
14759
  return;
14656
14760
  }
14657
- await channel.sendText(chatId, "Thinking...", "plain");
14761
+ await channel.sendText(chatId, "Thinking...", { parseMode: "plain" });
14658
14762
  const mode = getMode(chatId);
14659
14763
  const vModel = resolveModel(chatId);
14660
14764
  const vVerbose = getVerboseLevel(chatId);
@@ -14666,7 +14770,7 @@ async function handleVoice(msg, channel) {
14666
14770
  await sendResponse(chatId, channel, voiceResponse, msg.messageId);
14667
14771
  } catch (err) {
14668
14772
  error("[router] Voice error:", err);
14669
- await channel.sendText(chatId, `Voice processing error: ${errorMessage(err)}`, "plain");
14773
+ await channel.sendText(chatId, `Voice processing error: ${errorMessage(err)}`, { parseMode: "plain" });
14670
14774
  }
14671
14775
  }
14672
14776
  async function handleMedia(msg, channel) {
@@ -14674,15 +14778,15 @@ async function handleMedia(msg, channel) {
14674
14778
  const mBackendId = getBackend(chatId) ?? "claude";
14675
14779
  const mLimitMsg = checkBackendLimits(mBackendId);
14676
14780
  if (mLimitMsg) {
14677
- await channel.sendText(chatId, mLimitMsg, "plain");
14781
+ await channel.sendText(chatId, mLimitMsg, { parseMode: "plain" });
14678
14782
  return;
14679
14783
  }
14680
- await channel.sendText(chatId, "Processing your file...", "plain");
14784
+ await channel.sendText(chatId, "Processing your file...", { parseMode: "plain" });
14681
14785
  try {
14682
14786
  if (msg.type === "video") {
14683
14787
  const fileId = msg.metadata?.fileId ?? fileName;
14684
14788
  if (!fileId) {
14685
- await channel.sendText(chatId, "Could not retrieve video file ID.", "plain");
14789
+ await channel.sendText(chatId, "Could not retrieve video file ID.", { parseMode: "plain" });
14686
14790
  return;
14687
14791
  }
14688
14792
  const videoBuffer = await channel.downloadFile(fileId);
@@ -14774,11 +14878,12 @@ ${content}
14774
14878
  await sendResponse(chatId, channel, mediaResponse, msg.messageId);
14775
14879
  } catch (err) {
14776
14880
  error("[router] Media error:", err);
14777
- await channel.sendText(chatId, `Error processing file: ${errorMessage(err)}`, "plain");
14881
+ await channel.sendText(chatId, `Error processing file: ${errorMessage(err)}`, { parseMode: "plain" });
14778
14882
  }
14779
14883
  }
14780
14884
  async function handleText(msg, channel) {
14781
- const { chatId, text } = msg;
14885
+ const { chatId } = msg;
14886
+ let { text } = msg;
14782
14887
  if (hasActiveProfile(chatId)) {
14783
14888
  await handleProfileText(chatId, text, channel);
14784
14889
  return;
@@ -14786,7 +14891,7 @@ async function handleText(msg, channel) {
14786
14891
  if (hasPendingWizard(chatId)) {
14787
14892
  if (text.toLowerCase() === "cancel") {
14788
14893
  cancelWizard(chatId);
14789
- await channel.sendText(chatId, "Scheduling cancelled.", "plain");
14894
+ await channel.sendText(chatId, "Scheduling cancelled.", { parseMode: "plain" });
14790
14895
  return;
14791
14896
  }
14792
14897
  if (text.toLowerCase() === "confirm") {
@@ -14801,14 +14906,14 @@ async function handleText(msg, channel) {
14801
14906
  const content = rememberMatch[1];
14802
14907
  const trigger = content.split(/\s+/).slice(0, 3).join(" ");
14803
14908
  saveMemoryWithEmbedding(trigger, content, "semantic");
14804
- await channel.sendText(chatId, "Got it, I'll remember that.", "plain");
14909
+ await channel.sendText(chatId, "Got it, I'll remember that.", { parseMode: "plain" });
14805
14910
  return;
14806
14911
  }
14807
14912
  const model2 = resolveModel(chatId);
14808
14913
  const backendId = getBackend(chatId) ?? "claude";
14809
14914
  const limitMsg = checkBackendLimits(backendId);
14810
14915
  if (limitMsg) {
14811
- await channel.sendText(chatId, limitMsg, "plain");
14916
+ await channel.sendText(chatId, limitMsg, { parseMode: "plain" });
14812
14917
  return;
14813
14918
  }
14814
14919
  let intent = classifyIntent(text, chatId);
@@ -14830,18 +14935,32 @@ async function handleText(msg, channel) {
14830
14935
  const lastWarn = dashboardClawWarnings.get(chatId) ?? 0;
14831
14936
  if (Date.now() - lastWarn > 3e5) {
14832
14937
  dashboardClawWarnings.set(chatId, Date.now());
14833
- await channel.sendText(chatId, "\u26A0\uFE0F CC-Claw orchestration requires DASHBOARD_ENABLED=1. Using native agents.", "plain");
14938
+ await channel.sendText(chatId, "\u26A0\uFE0F CC-Claw orchestration requires DASHBOARD_ENABLED=1. Using native agents.", { parseMode: "plain" });
14834
14939
  }
14835
14940
  effectiveAgentMode = "native";
14836
14941
  }
14942
+ const { isSideQuest: hasSqPrefix, cleanText: sqCleanText } = parseSideQuestPrefix(text);
14943
+ if (hasSqPrefix && isChatBusy(chatId)) {
14944
+ const sqMsg = { ...msg, text: sqCleanText };
14945
+ handleSideQuest(chatId, sqMsg, channel).catch(
14946
+ (err) => error(`[router] Side quest error for ${chatId}:`, err)
14947
+ );
14948
+ return;
14949
+ }
14950
+ if (hasSqPrefix) {
14951
+ text = sqCleanText;
14952
+ }
14837
14953
  if (isChatBusy(chatId) && !bypassBusyCheck.delete(chatId)) {
14838
14954
  if (typeof channel.sendKeyboard === "function") {
14839
14955
  pendingInterrupts.set(chatId, { msg, channel });
14840
- await channel.sendKeyboard(chatId, "\u23F3 Agent is working on a request\u2026", [
14956
+ await channel.sendKeyboard(chatId, "\u23F3 Agent is working on a request. You can queue, interrupt, or start a parallel task.", [
14841
14957
  [
14842
- { label: "\u{1F4E5} Queue message", data: `interrupt:queue:${chatId}` },
14958
+ { label: "\u{1F4E5} Queue", data: `interrupt:queue:${chatId}` },
14843
14959
  { label: "\u26A1 Send now", data: `interrupt:now:${chatId}` }
14844
14960
  ],
14961
+ [
14962
+ { label: "\u{1F5FA} Side quest \u2014 run in parallel", data: `interrupt:sidequest:${chatId}` }
14963
+ ],
14845
14964
  [
14846
14965
  { label: "\u{1F5D1} Don't send", data: `interrupt:discard:${chatId}` }
14847
14966
  ]
@@ -14897,7 +15016,7 @@ async function handleText(msg, channel) {
14897
15016
  const toSlot = slots.find((s) => (s.label || `slot-${s.id}`) === to);
14898
15017
  const fromIcon = fromSlot?.slotType === "oauth" ? "\u{1F468}\u{1F3FD}\u200D\u{1F4BB}" : "\u{1F511}";
14899
15018
  const toIcon = toSlot?.slotType === "oauth" ? "\u{1F468}\u{1F3FD}\u200D\u{1F4BB}" : "\u{1F511}";
14900
- channel.sendText(cid, `\u26A0\uFE0F Quota reached on ${fromIcon} ${from} \u2014 switched to ${toIcon} ${to}. Context saved.`, "plain").catch(() => {
15019
+ channel.sendText(cid, `\u26A0\uFE0F Quota reached on ${fromIcon} ${from} \u2014 switched to ${toIcon} ${to}. Context saved.`, { parseMode: "plain" }).catch(() => {
14901
15020
  });
14902
15021
  }
14903
15022
  });
@@ -14933,6 +15052,9 @@ async function handleText(msg, channel) {
14933
15052
  return;
14934
15053
  }
14935
15054
  if (await handleResponseExhaustion(responseText, chatId, msg, channel)) return;
15055
+ if (activeSideQuests.has(chatId) && responseText && !responseText.startsWith("(No response")) {
15056
+ responseText = "\u{1F4CB} <b>Main task</b>\n\n" + responseText;
15057
+ }
14936
15058
  responseText = ensureReaction(responseText, cleanText || text);
14937
15059
  await sendResponse(chatId, channel, responseText, msg.messageId);
14938
15060
  try {
@@ -14959,7 +15081,7 @@ async function handleText(msg, channel) {
14959
15081
  ];
14960
15082
  await channel.sendKeyboard(chatId, `\u26A0\uFE0F No eligible slots for rotation mode (${modeLabel}). Switch backend?`, rows);
14961
15083
  } else {
14962
- await channel.sendText(chatId, `\u26A0\uFE0F No eligible slots for rotation mode (${modeLabel}).`, "plain");
15084
+ await channel.sendText(chatId, `\u26A0\uFE0F No eligible slots for rotation mode (${modeLabel}).`, { parseMode: "plain" });
14963
15085
  }
14964
15086
  return;
14965
15087
  } else if (errMsg.includes(GEMINI_ALL_SLOTS_COOLDOWN_MSG)) {
@@ -14971,7 +15093,7 @@ async function handleText(msg, channel) {
14971
15093
  ];
14972
15094
  await channel.sendKeyboard(chatId, `\u26A0\uFE0F All eligible slots in cooldown. Next available in ~${timeLeft}. Switch backend?`, rows);
14973
15095
  } else {
14974
- await channel.sendText(chatId, `\u26A0\uFE0F All eligible slots in cooldown. Next available in ~${timeLeft}.`, "plain");
15096
+ await channel.sendText(chatId, `\u26A0\uFE0F All eligible slots in cooldown. Next available in ~${timeLeft}.`, { parseMode: "plain" });
14975
15097
  }
14976
15098
  return;
14977
15099
  }
@@ -14980,7 +15102,7 @@ async function handleText(msg, channel) {
14980
15102
  if (await handleResponseExhaustion(errMsg, chatId, msg, channel)) return;
14981
15103
  }
14982
15104
  const userMsg = diagnoseAgentError(errMsg, chatId);
14983
- await channel.sendText(chatId, userMsg, "plain");
15105
+ await channel.sendText(chatId, userMsg, { parseMode: "plain" });
14984
15106
  } finally {
14985
15107
  typingActive = false;
14986
15108
  const pending = pendingInterrupts.get(chatId);
@@ -15006,6 +15128,81 @@ After installing, restart the service: cc-claw service restart`;
15006
15128
  }
15007
15129
  return `Error: ${msg}`;
15008
15130
  }
15131
+ async function handleSideQuest(parentChatId, msg, channel) {
15132
+ const sqId = `sq:${parentChatId}:${randomUUID3()}`;
15133
+ const active = activeSideQuests.get(parentChatId) ?? /* @__PURE__ */ new Set();
15134
+ if (active.size >= MAX_SIDE_QUESTS) {
15135
+ await channel.sendText(parentChatId, `Side quest slots full (${active.size} running). Queue this or wait for one to finish.`, { parseMode: "plain" });
15136
+ return;
15137
+ }
15138
+ active.add(sqId);
15139
+ activeSideQuests.set(parentChatId, active);
15140
+ await channel.sendKeyboard?.(parentChatId, "\u{1F5FA} Starting side quest\u2026", [
15141
+ [{ label: "\u274C Cancel", data: `sq:cancel:${sqId}` }]
15142
+ ]);
15143
+ const startTime = Date.now();
15144
+ let typingActive = true;
15145
+ const typingLoop = async () => {
15146
+ while (typingActive) {
15147
+ try {
15148
+ await channel.sendTyping?.(parentChatId);
15149
+ } catch {
15150
+ }
15151
+ await new Promise((r) => setTimeout(r, 4e3));
15152
+ }
15153
+ };
15154
+ typingLoop().catch(() => {
15155
+ });
15156
+ try {
15157
+ const backend2 = getBackend(parentChatId);
15158
+ const model2 = getModel(parentChatId);
15159
+ const permMode = getMode(parentChatId);
15160
+ const response = await askAgent(sqId, msg.text ?? "", {
15161
+ cwd: getCwd(parentChatId) ?? void 0,
15162
+ model: model2 ?? void 0,
15163
+ permMode,
15164
+ backend: backend2 ?? void 0,
15165
+ settingsSourceChatId: parentChatId,
15166
+ agentMode: "native",
15167
+ maxTurns: 10,
15168
+ timeoutMs: 3e5,
15169
+ bootstrapTier: "full"
15170
+ });
15171
+ typingActive = false;
15172
+ const userText = msg.text ?? "";
15173
+ const truncated = userText.length > 60 ? userText.slice(0, 57) + "\u2026" : userText;
15174
+ const header2 = `\u{1F5FA} <b>Side quest: "${truncated}"</b>
15175
+
15176
+ `;
15177
+ const responseText = header2 + (response.text ?? "");
15178
+ await sendResponse(parentChatId, channel, responseText, msg.messageId, typeof msg.messageId === "string" ? parseInt(msg.messageId) : void 0);
15179
+ const adapterForLog = backend2 ? getAdapter(backend2) : getAdapterForChat(parentChatId);
15180
+ appendToLog(parentChatId, `[side quest] ${userText}`, `[side quest] ${response.text ?? ""}`, adapterForLog.id, model2 ?? null, null);
15181
+ if (response.usage) {
15182
+ addUsage(parentChatId, response.usage.input, response.usage.output, response.usage.cacheRead, model2 ?? void 0, backend2 ?? void 0);
15183
+ }
15184
+ try {
15185
+ const { detectAndLogSignals: detectAndLogSignals2 } = await Promise.resolve().then(() => (init_detect(), detect_exports));
15186
+ const adapter = backend2 ? getAdapter(backend2) : getAdapterForChat(parentChatId);
15187
+ detectAndLogSignals2(parentChatId, userText, response.text ?? "", {
15188
+ backendId: adapter.id,
15189
+ model: model2 ?? adapter.defaultModel
15190
+ });
15191
+ } catch {
15192
+ }
15193
+ } catch (err) {
15194
+ typingActive = false;
15195
+ await channel.sendText(parentChatId, `\u{1F5FA} Side quest failed: ${err.message}`, { parseMode: "plain" });
15196
+ } finally {
15197
+ typingActive = false;
15198
+ const activeSet = activeSideQuests.get(parentChatId);
15199
+ if (activeSet) {
15200
+ activeSet.delete(sqId);
15201
+ if (activeSet.size === 0) activeSideQuests.delete(parentChatId);
15202
+ }
15203
+ clearSession(sqId);
15204
+ }
15205
+ }
15009
15206
  function formatToolStart(toolName, input, level) {
15010
15207
  if (level === "normal") {
15011
15208
  const path = input.file_path ?? input.path ?? input.file ?? "";
@@ -15078,11 +15275,11 @@ function makeToolActionCallback(chatId, channel, level) {
15078
15275
  return async (toolName, input, result) => {
15079
15276
  if (result === void 0) {
15080
15277
  const msg = formatToolStart(toolName, input, level);
15081
- await channel.sendText(chatId, msg, "plain").catch(() => {
15278
+ await channel.sendText(chatId, msg, { parseMode: "plain" }).catch(() => {
15082
15279
  });
15083
15280
  } else if (level === "verbose") {
15084
15281
  const msg = formatToolResult(toolName, result);
15085
- if (msg) await channel.sendText(chatId, msg, "plain").catch(() => {
15282
+ if (msg) await channel.sendText(chatId, msg, { parseMode: "plain" }).catch(() => {
15086
15283
  });
15087
15284
  }
15088
15285
  };
@@ -15133,11 +15330,11 @@ async function processImageGenerations(chatId, channel, text) {
15133
15330
  const name = result.filePath.split("/").pop() ?? "image.png";
15134
15331
  await channel.sendFile(chatId, file, name);
15135
15332
  if (result.text) {
15136
- await channel.sendText(chatId, result.text, "plain");
15333
+ await channel.sendText(chatId, result.text, { parseMode: "plain" });
15137
15334
  }
15138
15335
  } catch (err) {
15139
15336
  error(`[router] Image generation failed for "${prompt.slice(0, 50)}":`, err);
15140
- await channel.sendText(chatId, `Image generation failed: ${errorMessage(err)}`, "plain");
15337
+ await channel.sendText(chatId, `Image generation failed: ${errorMessage(err)}`, { parseMode: "plain" });
15141
15338
  }
15142
15339
  }
15143
15340
  return text.replace(pattern, "").trim();
@@ -15164,7 +15361,7 @@ async function processReaction(chatId, channel, text, messageId) {
15164
15361
  }
15165
15362
  return text.replace(/\[REACT:(.+?)\]/g, "").trim();
15166
15363
  }
15167
- async function sendResponse(chatId, channel, text, messageId) {
15364
+ async function sendResponse(chatId, channel, text, messageId, replyToMessageId) {
15168
15365
  text = await processReaction(chatId, channel, text, messageId);
15169
15366
  const { cleanText: afterUpdates, updates } = extractUserUpdates(text);
15170
15367
  for (const { key, value } of updates) {
@@ -15209,14 +15406,14 @@ async function sendResponse(chatId, channel, text, messageId) {
15209
15406
  error("[router] TTS failed, falling back to text:", err);
15210
15407
  }
15211
15408
  }
15212
- await channel.sendText(chatId, cleanText);
15409
+ await channel.sendText(chatId, cleanText, replyToMessageId ? { replyToMessageId } : void 0);
15213
15410
  }
15214
15411
  function isImageExt(ext) {
15215
15412
  return ["jpg", "jpeg", "png", "gif", "webp", "bmp", "svg"].includes(ext);
15216
15413
  }
15217
15414
  async function sendVoiceConfigKeyboard(chatId, channel) {
15218
15415
  if (typeof channel.sendKeyboard !== "function") {
15219
- await channel.sendText(chatId, "Voice configuration requires an interactive channel (Telegram).", "plain");
15416
+ await channel.sendText(chatId, "Voice configuration requires an interactive channel (Telegram).", { parseMode: "plain" });
15220
15417
  return;
15221
15418
  }
15222
15419
  const config2 = getVoiceConfig(chatId);
@@ -15282,7 +15479,7 @@ async function sendBackendSwitchConfirmation(chatId, target, channel) {
15282
15479
  const current = getBackend(chatId);
15283
15480
  const targetAdapter = getAdapter(target);
15284
15481
  if (current === target) {
15285
- await channel.sendText(chatId, `Already using ${targetAdapter.displayName}.`, "plain");
15482
+ await channel.sendText(chatId, `Already using ${targetAdapter.displayName}.`, { parseMode: "plain" });
15286
15483
  return;
15287
15484
  }
15288
15485
  if (typeof channel.sendKeyboard === "function") {
@@ -15306,7 +15503,7 @@ async function doBackendSwitch(chatId, backendId, channel) {
15306
15503
  const targetAdapter = getAdapter(backendId);
15307
15504
  const pairCount = getMessagePairCount(chatId);
15308
15505
  if (pairCount >= 2) {
15309
- await channel.sendText(chatId, `\u23F3 Saving context...`, "plain");
15506
+ await channel.sendText(chatId, `\u23F3 Saving context...`, { parseMode: "plain" });
15310
15507
  }
15311
15508
  const summarized = await summarizeWithFallbackChain(chatId, backendId);
15312
15509
  const bridge = buildContextBridge(chatId);
@@ -15314,9 +15511,9 @@ async function doBackendSwitch(chatId, backendId, channel) {
15314
15511
  setPendingContextBridge(chatId, bridge);
15315
15512
  }
15316
15513
  if (summarized) {
15317
- await channel.sendText(chatId, "\u{1F4BE} Context saved \u2014 session summarized to memory.", "plain");
15514
+ await channel.sendText(chatId, "\u{1F4BE} Context saved \u2014 session summarized to memory.", { parseMode: "plain" });
15318
15515
  } else if (bridge) {
15319
- await channel.sendText(chatId, "\u{1F4AC} Context preserved.", "plain");
15516
+ await channel.sendText(chatId, "\u{1F4AC} Context preserved.", { parseMode: "plain" });
15320
15517
  }
15321
15518
  clearSession(chatId);
15322
15519
  clearModel(chatId);
@@ -15324,7 +15521,7 @@ async function doBackendSwitch(chatId, backendId, channel) {
15324
15521
  clearChatGeminiSlot(chatId);
15325
15522
  setBackend(chatId, backendId);
15326
15523
  logActivity(getDb(), { chatId, source: "telegram", eventType: "config_changed", summary: `Backend switched to ${targetAdapter.displayName}`, detail: { field: "backend", value: backendId } });
15327
- await channel.sendText(chatId, `\u2705 Switched to ${targetAdapter.displayName}. Ready!`, "plain");
15524
+ await channel.sendText(chatId, `\u2705 Switched to ${targetAdapter.displayName}. Ready!`, { parseMode: "plain" });
15328
15525
  }
15329
15526
  async function sendHistoryView(chatId, channel, filter) {
15330
15527
  const limit = filter.limit ?? 10;
@@ -15341,7 +15538,7 @@ async function sendHistoryView(chatId, channel, filter) {
15341
15538
  }
15342
15539
  const userMsgs = rows.filter((r) => r.role === "user").slice(0, limit);
15343
15540
  if (userMsgs.length === 0) {
15344
- await channel.sendText(chatId, "No conversation history found.", "plain");
15541
+ await channel.sendText(chatId, "No conversation history found.", { parseMode: "plain" });
15345
15542
  return;
15346
15543
  }
15347
15544
  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"];
@@ -15350,7 +15547,7 @@ async function sendHistoryView(chatId, channel, filter) {
15350
15547
  const preview = row.content.slice(0, 80) + (row.content.length > 80 ? "\u2026" : "");
15351
15548
  lines.push(`[${row.backend ?? "?"} \xB7 ${date}] ${preview}`);
15352
15549
  }
15353
- await channel.sendText(chatId, lines.join("\n"), "plain");
15550
+ await channel.sendText(chatId, lines.join("\n"), { parseMode: "plain" });
15354
15551
  const filterRow = [
15355
15552
  { label: "Show More (25)", data: "hist:recent:25" },
15356
15553
  { label: "Today", data: "hist:today" },
@@ -15366,7 +15563,7 @@ async function sendHistoryView(chatId, channel, filter) {
15366
15563
  async function sendMemoryPage(chatId, channel, page) {
15367
15564
  const memories = listMemories();
15368
15565
  if (memories.length === 0) {
15369
- await channel.sendText(chatId, "No memories stored yet.", "plain");
15566
+ await channel.sendText(chatId, "No memories stored yet.", { parseMode: "plain" });
15370
15567
  return;
15371
15568
  }
15372
15569
  const pageSize = 10;
@@ -15442,7 +15639,7 @@ async function sendHeartbeatKeyboard(chatId, channel) {
15442
15639
  async function sendForgetPicker(chatId, channel, page) {
15443
15640
  const memories = listMemories();
15444
15641
  if (memories.length === 0) {
15445
- await channel.sendText(chatId, "No memories to forget.", "plain");
15642
+ await channel.sendText(chatId, "No memories to forget.", { parseMode: "plain" });
15446
15643
  return;
15447
15644
  }
15448
15645
  const { text, buttons } = buildPaginatedKeyboard({
@@ -15478,7 +15675,7 @@ function getJobStatusLabel(job) {
15478
15675
  async function sendJobsBoard(chatId, channel, page) {
15479
15676
  const jobs = listJobs();
15480
15677
  if (jobs.length === 0) {
15481
- await channel.sendText(chatId, "No scheduled jobs.\n\nCreate one: /schedule <description>", "plain");
15678
+ await channel.sendText(chatId, "No scheduled jobs.\n\nCreate one: /schedule <description>", { parseMode: "plain" });
15482
15679
  return;
15483
15680
  }
15484
15681
  if (typeof channel.sendKeyboard !== "function") {
@@ -15489,7 +15686,7 @@ async function sendJobsBoard(chatId, channel, page) {
15489
15686
  return `#${j.id} [${status}] ${schedule2}${tz}
15490
15687
  ${j.description}`;
15491
15688
  });
15492
- await channel.sendText(chatId, lines.join("\n\n"), "plain");
15689
+ await channel.sendText(chatId, lines.join("\n\n"), { parseMode: "plain" });
15493
15690
  return;
15494
15691
  }
15495
15692
  const headerLines = [];
@@ -15534,7 +15731,7 @@ async function sendJobDetail(chatId, jobId, channel) {
15534
15731
  [{ label: "Refresh List", data: "job:back" }]
15535
15732
  ]);
15536
15733
  } else {
15537
- await channel.sendText(chatId, "Job not found.", "plain");
15734
+ await channel.sendText(chatId, "Job not found.", { parseMode: "plain" });
15538
15735
  }
15539
15736
  return;
15540
15737
  }
@@ -15560,7 +15757,7 @@ async function sendJobDetail(chatId, jobId, channel) {
15560
15757
  ];
15561
15758
  const text = lines.join("\n");
15562
15759
  if (typeof channel.sendKeyboard !== "function") {
15563
- await channel.sendText(chatId, text, "plain");
15760
+ await channel.sendText(chatId, text, { parseMode: "plain" });
15564
15761
  return;
15565
15762
  }
15566
15763
  const actionRow1 = [
@@ -15590,7 +15787,7 @@ async function sendJobRunsView(chatId, jobId, channel, page) {
15590
15787
  [{ label: "\u2190 Back to Job", data: `job:view:${jobId}` }]
15591
15788
  ]);
15592
15789
  } else {
15593
- await channel.sendText(chatId, msg, "plain");
15790
+ await channel.sendText(chatId, msg, { parseMode: "plain" });
15594
15791
  }
15595
15792
  return;
15596
15793
  }
@@ -15601,7 +15798,7 @@ async function sendJobRunsView(chatId, jobId, channel, page) {
15601
15798
  Error: ${r.error.slice(0, 100)}` : "";
15602
15799
  return `#${r.jobId} [${r.status}] ${formatLocalDateTime(r.startedAt)}${duration}${error3}`;
15603
15800
  });
15604
- await channel.sendText(chatId, lines.join("\n\n"), "plain");
15801
+ await channel.sendText(chatId, lines.join("\n\n"), { parseMode: "plain" });
15605
15802
  return;
15606
15803
  }
15607
15804
  const statusEmoji = {
@@ -15648,11 +15845,11 @@ async function sendJobPicker(chatId, channel, action) {
15648
15845
  const jobs = action === "resume" ? allJobs.filter((j) => j.active && !j.enabled) : action === "pause" ? allJobs.filter((j) => j.active && j.enabled) : allJobs.filter((j) => j.active);
15649
15846
  if (jobs.length === 0) {
15650
15847
  const msg = action === "resume" ? "No paused jobs." : action === "pause" ? "No active jobs to pause." : "No active jobs.";
15651
- await channel.sendText(chatId, msg, "plain");
15848
+ await channel.sendText(chatId, msg, { parseMode: "plain" });
15652
15849
  return;
15653
15850
  }
15654
15851
  if (typeof channel.sendKeyboard !== "function") {
15655
- await channel.sendText(chatId, `Usage: /${action} <job-id>`, "plain");
15852
+ await channel.sendText(chatId, `Usage: /${action} <job-id>`, { parseMode: "plain" });
15656
15853
  return;
15657
15854
  }
15658
15855
  const actionLabel = action.charAt(0).toUpperCase() + action.slice(1);
@@ -15723,7 +15920,7 @@ async function handleCallback(chatId, data, channel) {
15723
15920
  const previous = getBackend(chatId);
15724
15921
  if (chosen === previous) {
15725
15922
  const adapter = getAdapter(chosen);
15726
- await channel.sendText(chatId, `Already using ${adapter.displayName}.`, "plain");
15923
+ await channel.sendText(chatId, `Already using ${adapter.displayName}.`, { parseMode: "plain" });
15727
15924
  return;
15728
15925
  }
15729
15926
  await sendBackendSwitchConfirmation(chatId, chosen, channel);
@@ -15737,14 +15934,14 @@ async function handleCallback(chatId, data, channel) {
15737
15934
  if (!getAllBackendIds().includes(chosen)) return;
15738
15935
  await doBackendSwitch(chatId, chosen, channel);
15739
15936
  } else if (data.startsWith("backend_cancel:") || data === "backend_cancel") {
15740
- await channel.sendText(chatId, "Switch cancelled.", "plain");
15937
+ await channel.sendText(chatId, "Switch cancelled.", { parseMode: "plain" });
15741
15938
  } else if (data.startsWith("model:")) {
15742
15939
  const chosen = data.slice(6);
15743
15940
  let adapter;
15744
15941
  try {
15745
15942
  adapter = getAdapterForChat(chatId);
15746
15943
  } catch {
15747
- await channel.sendText(chatId, "No backend set. Use /backend first.", "plain");
15944
+ await channel.sendText(chatId, "No backend set. Use /backend first.", { parseMode: "plain" });
15748
15945
  return;
15749
15946
  }
15750
15947
  const modelInfo = adapter.availableModels[chosen];
@@ -15766,30 +15963,30 @@ Select thinking/effort level:`,
15766
15963
  thinkingButtons
15767
15964
  );
15768
15965
  } else {
15769
- await channel.sendText(chatId, `Model set to ${modelInfo.label}. Session continues.`, "plain");
15966
+ await channel.sendText(chatId, `Model set to ${modelInfo.label}. Session continues.`, { parseMode: "plain" });
15770
15967
  }
15771
15968
  } else {
15772
- await channel.sendText(chatId, `Model switched to ${modelInfo.label}. Session continues.`, "plain");
15969
+ await channel.sendText(chatId, `Model switched to ${modelInfo.label}. Session continues.`, { parseMode: "plain" });
15773
15970
  }
15774
15971
  } else if (data.startsWith("thinking:")) {
15775
15972
  const level = data.slice(9);
15776
15973
  setThinkingLevel(chatId, level);
15777
15974
  logActivity(getDb(), { chatId, source: "telegram", eventType: "config_changed", summary: `Thinking level set to ${level}`, detail: { field: "thinking", value: level } });
15778
15975
  const label2 = level === "auto" ? "Auto" : level.replace("_", " ").replace(/\b\w/g, (c) => c.toUpperCase());
15779
- await channel.sendText(chatId, `Thinking level set to: ${label2}`, "plain");
15976
+ await channel.sendText(chatId, `Thinking level set to: ${label2}`, { parseMode: "plain" });
15780
15977
  } else if (data.startsWith("summarizer:")) {
15781
15978
  const rest = data.slice(11);
15782
15979
  if (rest === "auto") {
15783
15980
  clearSummarizer(chatId);
15784
- await channel.sendText(chatId, "Summarizer set to auto (uses active backend).", "plain");
15981
+ await channel.sendText(chatId, "Summarizer set to auto (uses active backend).", { parseMode: "plain" });
15785
15982
  } else if (rest === "off") {
15786
15983
  setSummarizer(chatId, "off", null);
15787
- await channel.sendText(chatId, "Session summarization disabled.", "plain");
15984
+ await channel.sendText(chatId, "Session summarization disabled.", { parseMode: "plain" });
15788
15985
  } else {
15789
15986
  const [bk, ...modelParts] = rest.split(":");
15790
15987
  const mdl = modelParts.join(":") || null;
15791
15988
  setSummarizer(chatId, bk, mdl);
15792
- await channel.sendText(chatId, `Summarizer pinned to ${bk}:${mdl ?? "default"}.`, "plain");
15989
+ await channel.sendText(chatId, `Summarizer pinned to ${bk}:${mdl ?? "default"}.`, { parseMode: "plain" });
15793
15990
  }
15794
15991
  } else if (data.startsWith("perms:")) {
15795
15992
  let chosen = data.slice(6);
@@ -15797,7 +15994,7 @@ Select thinking/effort level:`,
15797
15994
  if (!PERM_MODES[chosen]) return;
15798
15995
  const previous = getMode(chatId);
15799
15996
  if (chosen === previous) {
15800
- await channel.sendText(chatId, `Already in ${chosen} mode.`, "plain");
15997
+ await channel.sendText(chatId, `Already in ${chosen} mode.`, { parseMode: "plain" });
15801
15998
  return;
15802
15999
  }
15803
16000
  setMode(chatId, chosen);
@@ -15806,14 +16003,14 @@ Select thinking/effort level:`,
15806
16003
  chatId,
15807
16004
  `Permission mode: ${chosen}
15808
16005
  ${PERM_MODES[chosen]}`,
15809
- "plain"
16006
+ { parseMode: "plain" }
15810
16007
  );
15811
16008
  } else if (data.startsWith("perm:escalate:")) {
15812
16009
  const targetMode = data.slice(14);
15813
16010
  if (!PERM_MODES[targetMode]) return;
15814
16011
  setMode(chatId, targetMode);
15815
16012
  logActivity(getDb(), { chatId, source: "telegram", eventType: "config_changed", summary: `Permission escalated to ${targetMode}`, detail: { field: "permissions", value: targetMode } });
15816
- await channel.sendText(chatId, `Switched to ${targetMode} mode. Re-processing your request...`, "plain");
16013
+ await channel.sendText(chatId, `Switched to ${targetMode} mode. Re-processing your request...`, { parseMode: "plain" });
15817
16014
  const pending = getPendingEscalation(chatId);
15818
16015
  if (pending) {
15819
16016
  removePendingEscalation(chatId);
@@ -15821,12 +16018,12 @@ ${PERM_MODES[chosen]}`,
15821
16018
  }
15822
16019
  } else if (data === "perm:deny") {
15823
16020
  removePendingEscalation(chatId);
15824
- await channel.sendText(chatId, "Keeping current mode.", "plain");
16021
+ await channel.sendText(chatId, "Keeping current mode.", { parseMode: "plain" });
15825
16022
  } else if (data.startsWith("verbose:")) {
15826
16023
  const chosen = data.slice(8);
15827
16024
  if (!VERBOSE_LEVELS[chosen]) return;
15828
16025
  setVerboseLevel(chatId, chosen);
15829
- await channel.sendText(chatId, `Tool visibility: ${VERBOSE_LEVELS[chosen]}`, "plain");
16026
+ await channel.sendText(chatId, `Tool visibility: ${VERBOSE_LEVELS[chosen]}`, { parseMode: "plain" });
15830
16027
  } else if (data.startsWith("tool:toggle:")) {
15831
16028
  const toolName = data.slice(12);
15832
16029
  if (!ALL_TOOLS.includes(toolName)) return;
@@ -15834,11 +16031,11 @@ ${PERM_MODES[chosen]}`,
15834
16031
  await channel.sendText(
15835
16032
  chatId,
15836
16033
  `${newState ? "\u2705" : "\u274C"} ${toolName} ${newState ? "enabled" : "disabled"}`,
15837
- "plain"
16034
+ { parseMode: "plain" }
15838
16035
  );
15839
16036
  } else if (data === "tool:reset") {
15840
16037
  resetTools(chatId);
15841
- await channel.sendText(chatId, "Tool configuration reset \u2014 all tools enabled.", "plain");
16038
+ await channel.sendText(chatId, "Tool configuration reset \u2014 all tools enabled.", { parseMode: "plain" });
15842
16039
  } else if (data.startsWith("sched:")) {
15843
16040
  await handleWizardCallback(chatId, data, channel);
15844
16041
  } else if (data.startsWith("profile:")) {
@@ -15847,9 +16044,9 @@ ${PERM_MODES[chosen]}`,
15847
16044
  const parts = data.slice(11).split(":");
15848
16045
  if (parts[0] === "yes" && parts.length >= 3) {
15849
16046
  appendToUserProfile(parts[1], parts.slice(2).join(":"));
15850
- await channel.sendText(chatId, `Preference saved: ${parts[1]}`, "plain");
16047
+ await channel.sendText(chatId, `Preference saved: ${parts[1]}`, { parseMode: "plain" });
15851
16048
  } else {
15852
- await channel.sendText(chatId, "Preference not saved.", "plain");
16049
+ await channel.sendText(chatId, "Preference not saved.", { parseMode: "plain" });
15853
16050
  }
15854
16051
  } else if (data.startsWith("shell:")) {
15855
16052
  const parts = data.split(":");
@@ -15858,7 +16055,7 @@ ${PERM_MODES[chosen]}`,
15858
16055
  if (action === "confirm") {
15859
16056
  const pending = getPendingCommand(id);
15860
16057
  if (!pending) {
15861
- await channel.sendText(chatId, "Confirmation expired. Please re-send the command.", "plain");
16058
+ await channel.sendText(chatId, "Confirmation expired. Please re-send the command.", { parseMode: "plain" });
15862
16059
  return;
15863
16060
  }
15864
16061
  removePendingCommand(id);
@@ -15869,27 +16066,27 @@ ${PERM_MODES[chosen]}`,
15869
16066
  }
15870
16067
  } else if (action === "cancel") {
15871
16068
  removePendingCommand(id);
15872
- await channel.sendText(chatId, "Command cancelled.", "plain");
16069
+ await channel.sendText(chatId, "Command cancelled.", { parseMode: "plain" });
15873
16070
  }
15874
16071
  } else if (data.startsWith("cwd:")) {
15875
16072
  const parts = data.split(":");
15876
16073
  const action = parts[1];
15877
16074
  const targetChatId = parts.slice(2).join(":");
15878
16075
  if (action === "keep") {
15879
- await channel.sendText(chatId, "Session kept. The agent will continue with existing context.", "plain");
16076
+ await channel.sendText(chatId, "Session kept. The agent will continue with existing context.", { parseMode: "plain" });
15880
16077
  } else if (action === "summarize") {
15881
16078
  await summarizeSession(targetChatId);
15882
16079
  clearSession(targetChatId);
15883
- await channel.sendText(chatId, "Session summarized and reset. Context preserved in memory.", "plain");
16080
+ await channel.sendText(chatId, "Session summarized and reset. Context preserved in memory.", { parseMode: "plain" });
15884
16081
  } else if (action === "reset") {
15885
16082
  clearSession(targetChatId);
15886
- await channel.sendText(chatId, "Session reset. Clean slate.", "plain");
16083
+ await channel.sendText(chatId, "Session reset. Clean slate.", { parseMode: "plain" });
15887
16084
  }
15888
16085
  } else if (data.startsWith("cwdpick:")) {
15889
16086
  const alias = data.slice(8);
15890
16087
  const bookmark = getBookmark(chatId, alias);
15891
16088
  if (!bookmark) {
15892
- await channel.sendText(chatId, `Bookmark '${alias}' no longer exists.`, "plain");
16089
+ await channel.sendText(chatId, `Bookmark '${alias}' no longer exists.`, { parseMode: "plain" });
15893
16090
  return;
15894
16091
  }
15895
16092
  setCwd(chatId, bookmark.path);
@@ -15904,22 +16101,36 @@ ${PERM_MODES[chosen]}`,
15904
16101
  if (action === "now" && pending) {
15905
16102
  pendingInterrupts.delete(targetChatId);
15906
16103
  stopAgent(targetChatId);
15907
- await channel.sendText(chatId, "\u26A1 Stopping current task and processing your message\u2026", "plain");
16104
+ await channel.sendText(chatId, "\u26A1 Stopping current task and processing your message\u2026", { parseMode: "plain" });
15908
16105
  bypassBusyCheck.add(targetChatId);
15909
16106
  handleMessage(pending.msg, pending.channel).catch(() => {
15910
16107
  });
15911
16108
  } else if (action === "queue" && pending) {
15912
16109
  pendingInterrupts.delete(targetChatId);
15913
16110
  bypassBusyCheck.add(targetChatId);
15914
- await channel.sendText(chatId, "\u{1F4E5} Message queued \u2014 will process after current task.", "plain");
16111
+ await channel.sendText(chatId, "\u{1F4E5} Message queued \u2014 will process after current task.", { parseMode: "plain" });
15915
16112
  handleMessage(pending.msg, pending.channel).catch(() => {
15916
16113
  });
16114
+ } else if (action === "sidequest") {
16115
+ const pending2 = pendingInterrupts.get(targetChatId);
16116
+ if (pending2) {
16117
+ pendingInterrupts.delete(targetChatId);
16118
+ handleSideQuest(targetChatId, pending2.msg, pending2.channel).catch(
16119
+ (err) => error(`[router] Side quest error for ${targetChatId}:`, err)
16120
+ );
16121
+ } else {
16122
+ await channel.sendText(chatId, "Main task finished \u2014 your message was already processed.", { parseMode: "plain" });
16123
+ }
15917
16124
  } else if (action === "discard") {
15918
16125
  pendingInterrupts.delete(targetChatId);
15919
- await channel.sendText(chatId, "\u{1F5D1} Message discarded.", "plain");
16126
+ await channel.sendText(chatId, "\u{1F5D1} Message discarded.", { parseMode: "plain" });
15920
16127
  } else {
15921
- await channel.sendText(chatId, "Message already processed or expired.", "plain");
16128
+ await channel.sendText(chatId, "Message already processed or expired.", { parseMode: "plain" });
15922
16129
  }
16130
+ } else if (data.startsWith("sq:cancel:")) {
16131
+ const sqId = data.slice("sq:cancel:".length);
16132
+ stopAgent(sqId);
16133
+ await channel.sendText(chatId, "\u{1F5FA} Side quest cancelled.", { parseMode: "plain" });
15923
16134
  } else if (data.startsWith("fallback:")) {
15924
16135
  const parts = data.split(":");
15925
16136
  const targetBackend = parts[1];
@@ -15927,7 +16138,7 @@ ${PERM_MODES[chosen]}`,
15927
16138
  const pendingMsg = pendingFallbackMessages.get(targetChatId);
15928
16139
  if (targetBackend === "wait") {
15929
16140
  pendingFallbackMessages.delete(targetChatId);
15930
- await channel.sendText(chatId, "OK \u2014 you can switch manually with /backend when ready.", "plain");
16141
+ await channel.sendText(chatId, "OK \u2014 you can switch manually with /backend when ready.", { parseMode: "plain" });
15931
16142
  } else if (pendingMsg) {
15932
16143
  pendingFallbackMessages.delete(targetChatId);
15933
16144
  if (pendingMsg.agentMode) pendingMsg.msg.agentMode = pendingMsg.agentMode;
@@ -15937,10 +16148,10 @@ ${PERM_MODES[chosen]}`,
15937
16148
  clearSession(targetChatId);
15938
16149
  setBackend(targetChatId, targetBackend);
15939
16150
  const adapter = getAdapter(targetBackend);
15940
- await channel.sendText(chatId, `Switched to ${adapter.displayName}. Resending your message\u2026`, "plain");
16151
+ await channel.sendText(chatId, `Switched to ${adapter.displayName}. Resending your message\u2026`, { parseMode: "plain" });
15941
16152
  await handleMessage(pendingMsg.msg, pendingMsg.channel);
15942
16153
  } else {
15943
- await channel.sendText(chatId, "Fallback expired. Use /backend to switch manually.", "plain");
16154
+ await channel.sendText(chatId, "Fallback expired. Use /backend to switch manually.", { parseMode: "plain" });
15944
16155
  }
15945
16156
  } else if (data.startsWith("voice:")) {
15946
16157
  const action = data.slice(6);
@@ -15948,7 +16159,7 @@ ${PERM_MODES[chosen]}`,
15948
16159
  const current = isVoiceEnabled(chatId);
15949
16160
  const desired = action === "on";
15950
16161
  if (current !== desired) toggleVoice(chatId);
15951
- await channel.sendText(chatId, desired ? "\u{1F50A} Voice responses enabled." : "\u{1F507} Voice responses disabled.", "plain");
16162
+ await channel.sendText(chatId, desired ? "\u{1F50A} Voice responses enabled." : "\u{1F507} Voice responses disabled.", { parseMode: "plain" });
15952
16163
  }
15953
16164
  } else if (data.startsWith("style:")) {
15954
16165
  const selectedStyle = data.split(":")[1];
@@ -15963,7 +16174,7 @@ ${PERM_MODES[chosen]}`,
15963
16174
  ]
15964
16175
  ]);
15965
16176
  }
15966
- await channel.sendText(chatId, `Response style set to: ${selectedStyle}`, "plain");
16177
+ await channel.sendText(chatId, `Response style set to: ${selectedStyle}`, { parseMode: "plain" });
15967
16178
  }
15968
16179
  } else if (data.startsWith("agentmode:")) {
15969
16180
  const mode = data.split(":")[1];
@@ -15974,7 +16185,7 @@ ${PERM_MODES[chosen]}`,
15974
16185
  } catch {
15975
16186
  }
15976
16187
  clearSession(chatId);
15977
- await channel.sendText(chatId, `Agent mode set to <b>${mode}</b>. Session cleared.`, "html");
16188
+ await channel.sendText(chatId, `Agent mode set to <b>${mode}</b>. Session cleared.`, { parseMode: "html" });
15978
16189
  }
15979
16190
  return;
15980
16191
  } else if (data.startsWith("agents:")) {
@@ -15985,11 +16196,11 @@ ${PERM_MODES[chosen]}`,
15985
16196
  const agents2 = listActiveAgents(db3);
15986
16197
  const match = agents2.find((a) => a.id.startsWith(shortId));
15987
16198
  if (!match) {
15988
- await channel.sendText(chatId, `Agent ${shortId} not found or already stopped. Use /agents to refresh.`, "plain");
16199
+ await channel.sendText(chatId, `Agent ${shortId} not found or already stopped. Use /agents to refresh.`, { parseMode: "plain" });
15989
16200
  return;
15990
16201
  }
15991
16202
  const ok = cancelAgent(match.id);
15992
- await channel.sendText(chatId, ok ? `Agent ${match.id.slice(0, 8)} cancelled.` : "Could not cancel agent.", "plain");
16203
+ await channel.sendText(chatId, ok ? `Agent ${match.id.slice(0, 8)} cancelled.` : "Could not cancel agent.", { parseMode: "plain" });
15993
16204
  } else if (rest === "tasks") {
15994
16205
  const synth = { chatId, messageId: "", text: "", senderName: "User", type: "command", source: "telegram", command: "tasks", commandArgs: "" };
15995
16206
  await handleCommand(synth, channel);
@@ -16003,16 +16214,16 @@ ${PERM_MODES[chosen]}`,
16003
16214
  const agents2 = listActiveAgents(db3);
16004
16215
  const match = agents2.find((a) => a.id.startsWith(shortId));
16005
16216
  if (!match) {
16006
- await channel.sendText(chatId, `Agent ${shortId} not found or already stopped.`, "plain");
16217
+ await channel.sendText(chatId, `Agent ${shortId} not found or already stopped.`, { parseMode: "plain" });
16007
16218
  return;
16008
16219
  }
16009
16220
  const ok = cancelAgent(match.id);
16010
- await channel.sendText(chatId, ok ? `Agent ${match.id.slice(0, 8)} cancelled.` : "Could not cancel agent.", "plain");
16221
+ await channel.sendText(chatId, ok ? `Agent ${match.id.slice(0, 8)} cancelled.` : "Could not cancel agent.", { parseMode: "plain" });
16011
16222
  } else if (rest === "all") {
16012
16223
  const count = cancelAllAgents(chatId);
16013
- await channel.sendText(chatId, count > 0 ? `Cancelled ${count} agent(s).` : "No active agents to cancel.", "plain");
16224
+ await channel.sendText(chatId, count > 0 ? `Cancelled ${count} agent(s).` : "No active agents to cancel.", { parseMode: "plain" });
16014
16225
  } else if (rest === "cancel") {
16015
- await channel.sendText(chatId, "Cancelled.", "plain");
16226
+ await channel.sendText(chatId, "Cancelled.", { parseMode: "plain" });
16016
16227
  }
16017
16228
  return;
16018
16229
  } else if (data.startsWith("tasks:")) {
@@ -16022,7 +16233,7 @@ ${PERM_MODES[chosen]}`,
16022
16233
  const db3 = getDb();
16023
16234
  const task = getTask(db3, taskId);
16024
16235
  if (!task) {
16025
- await channel.sendText(chatId, "Task not found or outdated. Use /tasks to refresh.", "plain");
16236
+ await channel.sendText(chatId, "Task not found or outdated. Use /tasks to refresh.", { parseMode: "plain" });
16026
16237
  return;
16027
16238
  }
16028
16239
  const STATUS_EMOJI_TASK = {
@@ -16046,7 +16257,7 @@ ${PERM_MODES[chosen]}`,
16046
16257
  task.result ? `
16047
16258
  Result: ${task.result.slice(0, 500)}` : ""
16048
16259
  ].filter(Boolean);
16049
- await channel.sendText(chatId, lines.join("\n"), "plain");
16260
+ await channel.sendText(chatId, lines.join("\n"), { parseMode: "plain" });
16050
16261
  }
16051
16262
  return;
16052
16263
  } else if (data.startsWith("grotation:")) {
@@ -16056,25 +16267,25 @@ Result: ${task.result.slice(0, 500)}` : ""
16056
16267
  if (mode === "accounts") {
16057
16268
  const oauthSlots = getGeminiSlots().filter((s) => s.enabled && s.slotType === "oauth");
16058
16269
  if (oauthSlots.length === 0) {
16059
- await channel.sendText(chatId, "\u26A0\uFE0F No OAuth accounts configured. Add one with <code>cc-claw gemini add-account</code> or choose a different mode.", "html");
16270
+ await channel.sendText(chatId, "\u26A0\uFE0F No OAuth accounts configured. Add one with <code>cc-claw gemini add-account</code> or choose a different mode.", { parseMode: "html" });
16060
16271
  return;
16061
16272
  }
16062
16273
  } else if (mode === "keys") {
16063
16274
  const keySlots = getGeminiSlots().filter((s) => s.enabled && s.slotType === "api_key");
16064
16275
  if (keySlots.length === 0) {
16065
- await channel.sendText(chatId, "\u26A0\uFE0F No API keys configured. Add one with <code>cc-claw gemini add-key</code> or choose a different mode.", "html");
16276
+ await channel.sendText(chatId, "\u26A0\uFE0F No API keys configured. Add one with <code>cc-claw gemini add-key</code> or choose a different mode.", { parseMode: "html" });
16066
16277
  return;
16067
16278
  }
16068
16279
  }
16069
16280
  setGeminiRotationMode(mode);
16070
16281
  const modeLabels = { off: "Off", all: "All", accounts: "\u{1F468}\u{1F3FD}\u200D\u{1F4BB} Accounts only", keys: "\u{1F511} Keys only" };
16071
- await channel.sendText(chatId, `Rotation mode set to <b>${modeLabels[mode]}</b>.`, "html");
16282
+ await channel.sendText(chatId, `Rotation mode set to <b>${modeLabels[mode]}</b>.`, { parseMode: "html" });
16072
16283
  return;
16073
16284
  } else if (data.startsWith("gslot:")) {
16074
16285
  const val = data.split(":")[1];
16075
16286
  if (val === "auto") {
16076
16287
  clearChatGeminiSlot(chatId);
16077
- await channel.sendText(chatId, "Gemini slot set to <b>\u{1F504} auto rotation</b>.", "html");
16288
+ await channel.sendText(chatId, "Gemini slot set to <b>\u{1F504} auto rotation</b>.", { parseMode: "html" });
16078
16289
  } else {
16079
16290
  const slotId = parseInt(val, 10);
16080
16291
  const slots = getGeminiSlots();
@@ -16083,14 +16294,14 @@ Result: ${task.result.slice(0, 500)}` : ""
16083
16294
  pinChatGeminiSlot(chatId, slotId);
16084
16295
  const label2 = slot.label || `slot-${slot.id}`;
16085
16296
  const icon = slot.slotType === "oauth" ? "\u{1F468}\u{1F3FD}\u200D\u{1F4BB}" : "\u{1F511}";
16086
- await channel.sendText(chatId, `Pinned to ${icon} <b>${label2}</b>`, "html");
16297
+ await channel.sendText(chatId, `Pinned to ${icon} <b>${label2}</b>`, { parseMode: "html" });
16087
16298
  }
16088
16299
  }
16089
16300
  return;
16090
16301
  } else if (data === "gopen:accounts") {
16091
16302
  const slots = getGeminiSlots();
16092
16303
  if (slots.length === 0) {
16093
- await channel.sendText(chatId, "No Gemini credentials configured.\nAdd with: <code>cc-claw gemini add-key</code> or <code>cc-claw gemini add-account</code>", "html");
16304
+ await channel.sendText(chatId, "No Gemini credentials configured.\nAdd with: <code>cc-claw gemini add-key</code> or <code>cc-claw gemini add-account</code>", { parseMode: "html" });
16094
16305
  return;
16095
16306
  }
16096
16307
  if (typeof channel.sendKeyboard === "function") {
@@ -16122,7 +16333,7 @@ Result: ${task.result.slice(0, 500)}` : ""
16122
16333
  await channel.sendText(
16123
16334
  chatId,
16124
16335
  value === "on" ? "\u{1F9E0} Model signature enabled. Each response will show the active model and thinking level." : "Model signature disabled.",
16125
- "plain"
16336
+ { parseMode: "plain" }
16126
16337
  );
16127
16338
  } else if (data.startsWith("vcfg:")) {
16128
16339
  const parts = data.slice(5).split(":");
@@ -16134,14 +16345,14 @@ Result: ${task.result.slice(0, 500)}` : ""
16134
16345
  await channel.sendText(
16135
16346
  chatId,
16136
16347
  "\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.",
16137
- "markdown"
16348
+ { parseMode: "markdown" }
16138
16349
  );
16139
16350
  }
16140
16351
  if (provider === "elevenlabs" && !process.env.ELEVENLABS_API_KEY) {
16141
16352
  await channel.sendText(
16142
16353
  chatId,
16143
16354
  "\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.",
16144
- "markdown"
16355
+ { parseMode: "markdown" }
16145
16356
  );
16146
16357
  }
16147
16358
  const defaultVoice = provider === "grok" ? "eve" : provider === "macos" ? "Samantha" : "21m00Tcm4TlvDq8ikWAM";
@@ -16153,7 +16364,7 @@ Result: ${task.result.slice(0, 500)}` : ""
16153
16364
  const config2 = getVoiceConfig(chatId);
16154
16365
  setVoiceProvider(chatId, config2.provider, voiceId);
16155
16366
  const voiceName = config2.provider === "elevenlabs" ? ELEVENLABS_VOICES[voiceId]?.name ?? voiceId : config2.provider === "macos" ? MACOS_VOICES[voiceId]?.name ?? voiceId : voiceId;
16156
- await channel.sendText(chatId, `\u2705 Voice set to: ${voiceName}`, "plain");
16367
+ await channel.sendText(chatId, `\u2705 Voice set to: ${voiceName}`, { parseMode: "plain" });
16157
16368
  }
16158
16369
  } else if (data.startsWith("skills:page:")) {
16159
16370
  const page = parseInt(data.slice(12), 10);
@@ -16172,7 +16383,7 @@ Result: ${task.result.slice(0, 500)}` : ""
16172
16383
  const skills2 = await discoverAllSkills();
16173
16384
  const skill = skillSource ? skills2.find((s) => s.name === skillName && s.source === skillSource) : skills2.find((s) => s.name === skillName);
16174
16385
  if (!skill) {
16175
- await channel.sendText(chatId, `Skill "${skillName}" not found.`, "plain");
16386
+ await channel.sendText(chatId, `Skill "${skillName}" not found.`, { parseMode: "plain" });
16176
16387
  return;
16177
16388
  }
16178
16389
  const activeBackend = getBackend(chatId) ?? "claude";
@@ -16180,13 +16391,13 @@ Result: ${task.result.slice(0, 500)}` : ""
16180
16391
  await channel.sendText(
16181
16392
  chatId,
16182
16393
  `Note: "${skillName}" lists compatible backends as [${skill.compatibleBackends.join(", ")}], but active backend is ${activeBackend}. Proceeding anyway.`,
16183
- "plain"
16394
+ { parseMode: "plain" }
16184
16395
  );
16185
16396
  }
16186
16397
  const raw = await readFile5(skill.filePath, "utf-8");
16187
16398
  const skillContent = stripFrontmatter2(raw);
16188
16399
  const tags = skill.sources.join(", ");
16189
- await channel.sendText(chatId, `Loading skill: ${skillName} [${tags}]...`, "plain");
16400
+ await channel.sendText(chatId, `Loading skill: ${skillName} [${tags}]...`, { parseMode: "plain" });
16190
16401
  const skillModel = resolveModel(chatId);
16191
16402
  const sMode = getMode(chatId);
16192
16403
  const sVerbose = getVerboseLevel(chatId);
@@ -16199,19 +16410,64 @@ Result: ${task.result.slice(0, 500)}` : ""
16199
16410
  const action = parts[1];
16200
16411
  const idStr = parts[2];
16201
16412
  switch (action) {
16413
+ case "menu": {
16414
+ const { getReflectionStatus: getRefStatus, getUnprocessedSignalCount: getUnprocessedSignalCount2, getPendingInsightCount: getPendingInsightCount2, getLastAnalysisTime: getLastAnalysisTime2 } = await Promise.resolve().then(() => (init_store4(), store_exports4));
16415
+ const { getDb: getDb2 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
16416
+ const menuActive = getRefStatus(getDb2(), chatId) === "active";
16417
+ if (!menuActive) {
16418
+ const { buildEvolveOnboardingKeyboard: buildEvolveOnboardingKeyboard2 } = await Promise.resolve().then(() => (init_propose(), propose_exports));
16419
+ const text = [
16420
+ "Self-Learning & Evolution",
16421
+ "\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501",
16422
+ "",
16423
+ "Teach your assistant to improve over time.",
16424
+ "",
16425
+ "When enabled, CC-Claw watches for corrections,",
16426
+ "preferences, and frustration in your messages,",
16427
+ "then proposes changes to its personality and",
16428
+ "behavior files (SOUL.md, USER.md).",
16429
+ "",
16430
+ "You review and approve every change."
16431
+ ].join("\n");
16432
+ await channel.sendKeyboard(chatId, text, buildEvolveOnboardingKeyboard2());
16433
+ } else {
16434
+ const { buildEvolveMenuKeyboard: buildEvolveMenuKeyboard2 } = await Promise.resolve().then(() => (init_propose(), propose_exports));
16435
+ const signals = getUnprocessedSignalCount2(getDb2(), chatId);
16436
+ const pending = getPendingInsightCount2(getDb2(), chatId);
16437
+ const lastTime = getLastAnalysisTime2(getDb2(), chatId);
16438
+ let lastText = "never";
16439
+ if (lastTime) {
16440
+ const diffMs = Date.now() - (/* @__PURE__ */ new Date(lastTime + "Z")).getTime();
16441
+ const diffHours = Math.floor(diffMs / 36e5);
16442
+ if (diffHours < 1) lastText = "< 1 hour ago";
16443
+ else if (diffHours < 24) lastText = `${diffHours}h ago`;
16444
+ else lastText = `${Math.floor(diffHours / 24)}d ago`;
16445
+ }
16446
+ const lines = [
16447
+ "Self-Learning & Evolution",
16448
+ "\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501",
16449
+ "",
16450
+ `\u2705 Active`,
16451
+ `Signals: ${signals} pending \xB7 Proposals: ${pending}`,
16452
+ `Last analysis: ${lastText}`
16453
+ ];
16454
+ await channel.sendKeyboard(chatId, lines.join("\n"), buildEvolveMenuKeyboard2({ pendingProposals: pending, unprocessedSignals: signals }));
16455
+ }
16456
+ break;
16457
+ }
16202
16458
  case "analyze": {
16203
16459
  const { runAnalysis: runAnalysis2 } = await Promise.resolve().then(() => (init_analyze(), analyze_exports));
16204
16460
  const { formatNightlySummary: formatNightlySummary2 } = await Promise.resolve().then(() => (init_propose(), propose_exports));
16205
- await channel.sendText(chatId, "Analyzing recent interactions...", "plain");
16461
+ await channel.sendText(chatId, "Analyzing recent interactions...", { parseMode: "plain" });
16206
16462
  try {
16207
- const insights = await runAnalysis2(chatId);
16463
+ const insights = await runAnalysis2(chatId, { force: true });
16208
16464
  if (insights.length === 0) {
16209
- await channel.sendText(chatId, "No new insights from recent interactions.", "plain");
16465
+ await channel.sendText(chatId, "No new insights from recent interactions.", { parseMode: "plain" });
16210
16466
  } else {
16211
- await channel.sendText(chatId, formatNightlySummary2(insights), "plain");
16467
+ await channel.sendText(chatId, formatNightlySummary2(insights), { parseMode: "plain" });
16212
16468
  }
16213
16469
  } catch (e) {
16214
- await channel.sendText(chatId, `Analysis failed: ${e}`, "plain");
16470
+ await channel.sendText(chatId, `Analysis failed: ${e}`, { parseMode: "plain" });
16215
16471
  }
16216
16472
  break;
16217
16473
  }
@@ -16221,7 +16477,7 @@ Result: ${task.result.slice(0, 500)}` : ""
16221
16477
  const { getDb: getDb2 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
16222
16478
  const pending = getPendingInsights2(getDb2(), chatId);
16223
16479
  if (pending.length === 0) {
16224
- await channel.sendText(chatId, "No pending proposals.", "plain");
16480
+ await channel.sendText(chatId, "No pending proposals.", { parseMode: "plain" });
16225
16481
  } else {
16226
16482
  for (const insight of pending.slice(0, 5)) {
16227
16483
  const card = formatProposalCard2(insight);
@@ -16229,7 +16485,7 @@ Result: ${task.result.slice(0, 500)}` : ""
16229
16485
  await channel.sendKeyboard(chatId, card, kb);
16230
16486
  }
16231
16487
  if (pending.length > 5) {
16232
- await channel.sendText(chatId, `${pending.length - 5} more proposals. Run /evolve again to see next batch.`, "plain");
16488
+ await channel.sendText(chatId, `${pending.length - 5} more proposals. Run /evolve again to see next batch.`, { parseMode: "plain" });
16233
16489
  }
16234
16490
  }
16235
16491
  break;
@@ -16240,25 +16496,25 @@ Result: ${task.result.slice(0, 500)}` : ""
16240
16496
  const { getDb: getDb2 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
16241
16497
  const insight = getInsightById2(getDb2(), parseInt(idStr, 10));
16242
16498
  if (insight?.proposedDiff) {
16243
- await channel.sendText(chatId, formatDiffCodeBlock2(insight.proposedDiff), "plain");
16499
+ await channel.sendText(chatId, formatDiffCodeBlock2(insight.proposedDiff), { parseMode: "plain" });
16244
16500
  }
16245
16501
  break;
16246
16502
  }
16247
16503
  case "apply": {
16248
16504
  const { applyInsight: applyInsight2 } = await Promise.resolve().then(() => (init_apply(), apply_exports));
16249
16505
  const result = await applyInsight2(parseInt(idStr, 10));
16250
- await channel.sendText(chatId, result.message, "plain");
16506
+ await channel.sendText(chatId, result.message, { parseMode: "plain" });
16251
16507
  break;
16252
16508
  }
16253
16509
  case "skip": {
16254
- await channel.sendText(chatId, "Skipped \u2014 will show again next review.", "plain");
16510
+ await channel.sendText(chatId, "Skipped \u2014 will show again next review.", { parseMode: "plain" });
16255
16511
  break;
16256
16512
  }
16257
16513
  case "reject": {
16258
16514
  const { updateInsightStatus: updateInsightStatus2 } = await Promise.resolve().then(() => (init_store4(), store_exports4));
16259
16515
  const { getDb: getDb2 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
16260
16516
  updateInsightStatus2(getDb2(), parseInt(idStr, 10), "rejected");
16261
- await channel.sendText(chatId, "Rejected. Won't propose similar changes.", "plain");
16517
+ await channel.sendText(chatId, "Rejected. Won't propose similar changes.", { parseMode: "plain" });
16262
16518
  break;
16263
16519
  }
16264
16520
  case "stats": {
@@ -16273,7 +16529,7 @@ Result: ${task.result.slice(0, 500)}` : ""
16273
16529
  if (drift && (drift.soulDrift > 0.5 || drift.userDrift > 0.5)) {
16274
16530
  report += "\n\nSOUL.md has changed significantly since reflection started.\nTap History in /evolve to review all applied changes.";
16275
16531
  }
16276
- await channel.sendText(chatId, report, "plain");
16532
+ await channel.sendText(chatId, report, { parseMode: "plain" });
16277
16533
  break;
16278
16534
  }
16279
16535
  case "history": {
@@ -16293,7 +16549,7 @@ Result: ${task.result.slice(0, 500)}` : ""
16293
16549
  msg += `#${i.id} [${i.category}] ${i.insight}
16294
16550
  `;
16295
16551
  });
16296
- await channel.sendText(chatId, msg, "plain");
16552
+ await channel.sendText(chatId, msg, { parseMode: "plain" });
16297
16553
  break;
16298
16554
  }
16299
16555
  case "toggle": {
@@ -16311,12 +16567,12 @@ Result: ${task.result.slice(0, 500)}` : ""
16311
16567
  setReflectionStatus2(getDb2(), chatId, "active", soul, user);
16312
16568
  const { logActivity: logActivity2 } = await Promise.resolve().then(() => (init_store3(), store_exports3));
16313
16569
  logActivity2(getDb2(), { chatId, source: "telegram", eventType: "reflection_unfrozen", summary: "Reflection enabled" });
16314
- await channel.sendText(chatId, "Self-learning enabled. Signal detection is now active.\nCreate a nightly cron job with /schedule to enable automatic analysis.", "plain");
16570
+ await channel.sendText(chatId, "\u2705 Self-learning enabled. Signal detection is now active.\nCreate a nightly cron job with /schedule to enable automatic analysis.", { parseMode: "plain" });
16315
16571
  } else {
16316
16572
  setReflectionStatus2(getDb2(), chatId, "frozen", null, null);
16317
16573
  const { logActivity: logActivity2 } = await Promise.resolve().then(() => (init_store3(), store_exports3));
16318
16574
  logActivity2(getDb2(), { chatId, source: "telegram", eventType: "reflection_frozen", summary: "Reflection disabled" });
16319
- await channel.sendText(chatId, "Self-learning disabled. No signals will be collected.", "plain");
16575
+ await channel.sendText(chatId, "\u26D4 Self-learning disabled. No signals will be collected.", { parseMode: "plain" });
16320
16576
  }
16321
16577
  break;
16322
16578
  }
@@ -16324,14 +16580,14 @@ Result: ${task.result.slice(0, 500)}` : ""
16324
16580
  if (idStr) {
16325
16581
  const { rollbackInsight: rollbackInsight2 } = await Promise.resolve().then(() => (init_apply(), apply_exports));
16326
16582
  const result = await rollbackInsight2(parseInt(idStr, 10));
16327
- await channel.sendText(chatId, result.message, "plain");
16583
+ await channel.sendText(chatId, result.message, { parseMode: "plain" });
16328
16584
  } else {
16329
16585
  const { getAppliedInsights: getAppliedInsights2 } = await Promise.resolve().then(() => (init_store4(), store_exports4));
16330
16586
  const { buildUndoKeyboard: buildUndoKeyboard2 } = await Promise.resolve().then(() => (init_propose(), propose_exports));
16331
16587
  const { getDb: getDb2 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
16332
16588
  const applied = getAppliedInsights2(getDb2(), chatId, 10);
16333
16589
  if (applied.length === 0) {
16334
- await channel.sendText(chatId, "No applied insights to undo.", "plain");
16590
+ await channel.sendText(chatId, "No applied insights to undo.", { parseMode: "plain" });
16335
16591
  } else {
16336
16592
  const kb = buildUndoKeyboard2(applied);
16337
16593
  await channel.sendKeyboard(chatId, "Select an insight to undo:", kb);
@@ -16344,7 +16600,7 @@ Result: ${task.result.slice(0, 500)}` : ""
16344
16600
  const { setReflectionModelConfig: setReflectionModelConfig2 } = await Promise.resolve().then(() => (init_store4(), store_exports4));
16345
16601
  const { getDb: getDb2 } = await Promise.resolve().then(() => (init_store5(), store_exports5));
16346
16602
  setReflectionModelConfig2(getDb2(), chatId, idStr);
16347
- await channel.sendText(chatId, `Analysis model set to: ${idStr}`, "plain");
16603
+ await channel.sendText(chatId, `Analysis model set to: ${idStr}`, { parseMode: "plain" });
16348
16604
  } else {
16349
16605
  const { getReflectionModelConfig: getReflectionModelConfig2 } = await Promise.resolve().then(() => (init_store4(), store_exports4));
16350
16606
  const { buildModelKeyboard: buildModelKeyboard2 } = await Promise.resolve().then(() => (init_propose(), propose_exports));
@@ -16362,16 +16618,16 @@ Result: ${task.result.slice(0, 500)}` : ""
16362
16618
  if (action === "all") {
16363
16619
  const pendingIds = getLoggedChatIds();
16364
16620
  if (pendingIds.length === 0) {
16365
- await channel.sendText(chatId, "No pending sessions to summarize.", "plain");
16621
+ await channel.sendText(chatId, "No pending sessions to summarize.", { parseMode: "plain" });
16366
16622
  return;
16367
16623
  }
16368
- await channel.sendText(chatId, `Summarizing ${pendingIds.length} pending session(s)...`, "plain");
16624
+ await channel.sendText(chatId, `Summarizing ${pendingIds.length} pending session(s)...`, { parseMode: "plain" });
16369
16625
  await summarizeAllPending();
16370
- await channel.sendText(chatId, `Done. ${pendingIds.length} session(s) summarized and saved to memory.`, "plain");
16626
+ await channel.sendText(chatId, `Done. ${pendingIds.length} session(s) summarized and saved to memory.`, { parseMode: "plain" });
16371
16627
  } else if (action === "undo") {
16372
16628
  const pending = pendingSummaryUndo.get(chatId);
16373
16629
  if (!pending) {
16374
- await channel.sendText(chatId, "Undo window expired.", "plain");
16630
+ await channel.sendText(chatId, "Undo window expired.", { parseMode: "plain" });
16375
16631
  return;
16376
16632
  }
16377
16633
  clearTimeout(pending.timer);
@@ -16389,20 +16645,20 @@ Result: ${task.result.slice(0, 500)}` : ""
16389
16645
  if (action === "go") {
16390
16646
  const { runAnalysis: runAnalysis2 } = await Promise.resolve().then(() => (init_analyze(), analyze_exports));
16391
16647
  const { formatNightlySummary: formatNightlySummary2 } = await Promise.resolve().then(() => (init_propose(), propose_exports));
16392
- await channel.sendText(chatId, "Analyzing recent interactions...", "plain");
16648
+ await channel.sendText(chatId, "Analyzing recent interactions...", { parseMode: "plain" });
16393
16649
  try {
16394
- const insights = await runAnalysis2(chatId);
16650
+ const insights = await runAnalysis2(chatId, { force: true });
16395
16651
  if (insights.length === 0) {
16396
- await channel.sendText(chatId, "No new insights from recent interactions.", "plain");
16652
+ await channel.sendText(chatId, "No new insights from recent interactions.", { parseMode: "plain" });
16397
16653
  } else {
16398
16654
  const items = insights.map((ins, i) => ({ id: i + 1, category: ins.category, insight: ins.insight }));
16399
- await channel.sendText(chatId, formatNightlySummary2(items) + "\n\nUse /evolve to review and apply proposals.", "plain");
16655
+ await channel.sendText(chatId, formatNightlySummary2(items) + "\n\nUse /evolve to review and apply proposals.", { parseMode: "plain" });
16400
16656
  }
16401
16657
  } catch (e) {
16402
- await channel.sendText(chatId, `Analysis failed: ${e}`, "plain");
16658
+ await channel.sendText(chatId, `Analysis failed: ${e}`, { parseMode: "plain" });
16403
16659
  }
16404
16660
  } else if (action === "cancel") {
16405
- await channel.sendText(chatId, "Reflection cancelled.", "plain");
16661
+ await channel.sendText(chatId, "Reflection cancelled.", { parseMode: "plain" });
16406
16662
  }
16407
16663
  return;
16408
16664
  } else if (data.startsWith("chats:")) {
@@ -16432,7 +16688,7 @@ Result: ${task.result.slice(0, 500)}` : ""
16432
16688
  } else if (action === "remove") {
16433
16689
  const aliases = getAllChatAliases();
16434
16690
  if (aliases.length === 0) {
16435
- await channel.sendText(chatId, "No aliases to remove.", "plain");
16691
+ await channel.sendText(chatId, "No aliases to remove.", { parseMode: "plain" });
16436
16692
  return;
16437
16693
  }
16438
16694
  const aliasButtons = aliases.map((a, i) => [{ label: a.alias, data: `chats:remove:${i}` }]);
@@ -16442,12 +16698,12 @@ Result: ${task.result.slice(0, 500)}` : ""
16442
16698
  const idx = parseInt(action.slice(7), 10);
16443
16699
  const aliases = getAllChatAliases();
16444
16700
  if (isNaN(idx) || idx < 0 || idx >= aliases.length) {
16445
- await channel.sendText(chatId, "This data is outdated. Use /chats to refresh.", "plain");
16701
+ await channel.sendText(chatId, "This data is outdated. Use /chats to refresh.", { parseMode: "plain" });
16446
16702
  return;
16447
16703
  }
16448
16704
  const alias = aliases[idx].alias;
16449
16705
  const removed = removeChatAlias(alias);
16450
- await channel.sendText(chatId, removed ? `Alias "${alias}" removed.` : `Alias "${alias}" not found.`, "plain");
16706
+ await channel.sendText(chatId, removed ? `Alias "${alias}" removed.` : `Alias "${alias}" not found.`, { parseMode: "plain" });
16451
16707
  }
16452
16708
  return;
16453
16709
  } else if (data.startsWith("hist:")) {
@@ -16478,7 +16734,7 @@ Result: ${task.result.slice(0, 500)}` : ""
16478
16734
  [{ label: "Refresh List", data: "forget:page:1" }]
16479
16735
  ]);
16480
16736
  } else {
16481
- await channel.sendText(chatId, "This memory no longer exists.", "plain");
16737
+ await channel.sendText(chatId, "This memory no longer exists.", { parseMode: "plain" });
16482
16738
  }
16483
16739
  return;
16484
16740
  }
@@ -16499,10 +16755,10 @@ Salience: ${memory2.salience.toFixed(2)} | Created: ${memory2.created_at.slice(0
16499
16755
  await channel.sendText(
16500
16756
  chatId,
16501
16757
  deleted ? "Memory deleted." : "Memory not found \u2014 already deleted.",
16502
- "plain"
16758
+ { parseMode: "plain" }
16503
16759
  );
16504
16760
  } else if (rest === "cancel") {
16505
- await channel.sendText(chatId, "Cancelled.", "plain");
16761
+ await channel.sendText(chatId, "Cancelled.", { parseMode: "plain" });
16506
16762
  }
16507
16763
  return;
16508
16764
  } else if (data.startsWith("mem:")) {
@@ -16519,7 +16775,7 @@ Salience: ${memory2.salience.toFixed(2)} | Created: ${memory2.created_at.slice(0
16519
16775
  [{ label: "Refresh List", data: "mem:page:1" }]
16520
16776
  ]);
16521
16777
  } else {
16522
- await channel.sendText(chatId, "This memory no longer exists.", "plain");
16778
+ await channel.sendText(chatId, "This memory no longer exists.", { parseMode: "plain" });
16523
16779
  }
16524
16780
  return;
16525
16781
  }
@@ -16544,13 +16800,13 @@ Salience: ${memory2.salience.toFixed(2)} | Created: ${memory2.created_at.slice(0
16544
16800
  await channel.sendText(
16545
16801
  chatId,
16546
16802
  deleted ? "Memory deleted." : "Memory not found \u2014 already deleted.",
16547
- "plain"
16803
+ { parseMode: "plain" }
16548
16804
  );
16549
16805
  } else if (rest.startsWith("forget:")) {
16550
16806
  const id = parseInt(rest.slice(7), 10);
16551
16807
  const memory2 = getMemoryById(id);
16552
16808
  if (!memory2) {
16553
- await channel.sendText(chatId, "Memory not found.", "plain");
16809
+ await channel.sendText(chatId, "Memory not found.", { parseMode: "plain" });
16554
16810
  return;
16555
16811
  }
16556
16812
  if (typeof channel.sendKeyboard === "function") {
@@ -16568,7 +16824,7 @@ Salience: ${memory2.salience.toFixed(2)} | Created: ${memory2.created_at.slice(0
16568
16824
  }
16569
16825
  } else if (rest.startsWith("edit:")) {
16570
16826
  const id = parseInt(rest.slice(5), 10);
16571
- await channel.sendText(chatId, `Type: /memory edit ${id} <new content>`, "plain");
16827
+ await channel.sendText(chatId, `Type: /memory edit ${id} <new content>`, { parseMode: "plain" });
16572
16828
  } else if (rest === "back") {
16573
16829
  await sendMemoryPage(chatId, channel, 1);
16574
16830
  } else if (rest === "showall") {
@@ -16606,7 +16862,7 @@ Salience: ${memory2.salience.toFixed(2)} | Created: ${memory2.created_at.slice(0
16606
16862
  if (rest === "undo") {
16607
16863
  const pending = pendingNewchatUndo.get(chatId);
16608
16864
  if (!pending) {
16609
- await channel.sendText(chatId, "Undo window expired.", "plain");
16865
+ await channel.sendText(chatId, "Undo window expired.", { parseMode: "plain" });
16610
16866
  return;
16611
16867
  }
16612
16868
  clearTimeout(pending.timer);
@@ -16637,13 +16893,13 @@ Salience: ${memory2.salience.toFixed(2)} | Created: ${memory2.created_at.slice(0
16637
16893
  clearBackendLimit(bid, win);
16638
16894
  }
16639
16895
  }
16640
- await channel.sendText(chatId, "All usage limits cleared.", "plain");
16896
+ await channel.sendText(chatId, "All usage limits cleared.", { parseMode: "plain" });
16641
16897
  await sendUnifiedUsage(chatId, channel, "session");
16642
16898
  } else if (rest.startsWith("limits:set:")) {
16643
16899
  const bid = rest.slice(11);
16644
16900
  await channel.sendText(chatId, `Set limit for ${bid}:
16645
16901
  /limits ${bid} daily <tokens>
16646
- Example: /limits ${bid} daily 500000`, "plain");
16902
+ Example: /limits ${bid} daily 500000`, { parseMode: "plain" });
16647
16903
  }
16648
16904
  return;
16649
16905
  } else if (data.startsWith("job:")) {
@@ -16655,14 +16911,14 @@ Example: /limits ${bid} daily 500000`, "plain");
16655
16911
  await sendJobDetail(chatId, id, channel);
16656
16912
  } else if (rest.startsWith("run:")) {
16657
16913
  const id = parseInt(rest.slice(4), 10);
16658
- await channel.sendText(chatId, `Triggering job #${id}...`, "plain");
16914
+ await channel.sendText(chatId, `Triggering job #${id}...`, { parseMode: "plain" });
16659
16915
  const result = await triggerJob(id);
16660
16916
  if (typeof channel.sendKeyboard === "function") {
16661
16917
  await channel.sendKeyboard(chatId, result, [
16662
16918
  [{ label: "\u2190 Back to Job", data: `job:view:${id}` }]
16663
16919
  ]);
16664
16920
  } else {
16665
- await channel.sendText(chatId, result, "plain");
16921
+ await channel.sendText(chatId, result, { parseMode: "plain" });
16666
16922
  }
16667
16923
  } else if (rest.startsWith("pause:")) {
16668
16924
  const id = parseInt(rest.slice(6), 10);
@@ -16670,7 +16926,7 @@ Example: /limits ${bid} daily 500000`, "plain");
16670
16926
  if (paused) {
16671
16927
  await sendJobDetail(chatId, id, channel);
16672
16928
  } else {
16673
- await channel.sendText(chatId, `Job #${id} not found.`, "plain");
16929
+ await channel.sendText(chatId, `Job #${id} not found.`, { parseMode: "plain" });
16674
16930
  }
16675
16931
  } else if (rest.startsWith("resume:")) {
16676
16932
  const id = parseInt(rest.slice(7), 10);
@@ -16678,22 +16934,22 @@ Example: /limits ${bid} daily 500000`, "plain");
16678
16934
  if (resumed) {
16679
16935
  await sendJobDetail(chatId, id, channel);
16680
16936
  } else {
16681
- await channel.sendText(chatId, `Job #${id} not found.`, "plain");
16937
+ await channel.sendText(chatId, `Job #${id} not found.`, { parseMode: "plain" });
16682
16938
  }
16683
16939
  } else if (rest.startsWith("cancel:confirm:")) {
16684
16940
  const id = parseInt(rest.slice(15), 10);
16685
16941
  const cancelled = cancelJob(id);
16686
16942
  if (cancelled) {
16687
- await channel.sendText(chatId, `Job #${id} cancelled.`, "plain");
16943
+ await channel.sendText(chatId, `Job #${id} cancelled.`, { parseMode: "plain" });
16688
16944
  await sendJobsBoard(chatId, channel, 1);
16689
16945
  } else {
16690
- await channel.sendText(chatId, `Job #${id} not found.`, "plain");
16946
+ await channel.sendText(chatId, `Job #${id} not found.`, { parseMode: "plain" });
16691
16947
  }
16692
16948
  } else if (rest.startsWith("cancel:")) {
16693
16949
  const id = parseInt(rest.slice(7), 10);
16694
16950
  const job = getJobById(id);
16695
16951
  if (!job) {
16696
- await channel.sendText(chatId, `Job #${id} not found.`, "plain");
16952
+ await channel.sendText(chatId, `Job #${id} not found.`, { parseMode: "plain" });
16697
16953
  return;
16698
16954
  }
16699
16955
  if (typeof channel.sendKeyboard === "function") {
@@ -16747,7 +17003,7 @@ Command: ${command}`,
16747
17003
  );
16748
17004
  } else {
16749
17005
  await channel.sendText(chatId, `\u26A0\uFE0F Destructive command blocked: ${command}
16750
- No keyboard available to confirm.`, "plain");
17006
+ No keyboard available to confirm.`, { parseMode: "plain" });
16751
17007
  }
16752
17008
  return;
16753
17009
  }
@@ -16764,9 +17020,9 @@ No keyboard available to confirm.`, "plain");
16764
17020
  if (shouldSendAsFile(formatted)) {
16765
17021
  const buffer = Buffer.from(result.output, "utf-8");
16766
17022
  await channel.sendFile(chatId, buffer, "output.txt", "text/plain");
16767
- await channel.sendText(chatId, `Output too long (${result.output.length} chars), sent as file.`, "plain");
17023
+ await channel.sendText(chatId, `Output too long (${result.output.length} chars), sent as file.`, { parseMode: "plain" });
16768
17024
  } else {
16769
- await channel.sendText(chatId, formatted, "html");
17025
+ await channel.sendText(chatId, formatted, { parseMode: "html" });
16770
17026
  }
16771
17027
  }
16772
17028
  async function handleRawShell(command, chatId, channel, skipGuard = false) {
@@ -16785,7 +17041,7 @@ Command: ${command}`,
16785
17041
  );
16786
17042
  } else {
16787
17043
  await channel.sendText(chatId, `\u26A0\uFE0F Destructive command blocked: ${command}
16788
- No keyboard available to confirm.`, "plain");
17044
+ No keyboard available to confirm.`, { parseMode: "plain" });
16789
17045
  }
16790
17046
  return;
16791
17047
  }
@@ -16802,9 +17058,9 @@ No keyboard available to confirm.`, "plain");
16802
17058
  if (shouldSendAsFile(formatted)) {
16803
17059
  const buffer = Buffer.from(result.output, "utf-8");
16804
17060
  await channel.sendFile(chatId, buffer, "output.txt", "text/plain");
16805
- await channel.sendText(chatId, `Output too long (${result.output.length} chars), sent as file.`, "plain");
17061
+ await channel.sendText(chatId, `Output too long (${result.output.length} chars), sent as file.`, { parseMode: "plain" });
16806
17062
  } else {
16807
- await channel.sendText(chatId, formatted, "plain");
17063
+ await channel.sendText(chatId, formatted, { parseMode: "plain" });
16808
17064
  }
16809
17065
  }
16810
17066
  async function sendCwdSessionChoice(chatId, path, channel) {
@@ -16823,7 +17079,7 @@ What would you like to do?`,
16823
17079
  );
16824
17080
  } else {
16825
17081
  await channel.sendText(chatId, `Working directory set to: ${path}
16826
- Session kept.`, "plain");
17082
+ Session kept.`, { parseMode: "plain" });
16827
17083
  }
16828
17084
  }
16829
17085
  function parseIntervalToMs(input) {
@@ -16884,7 +17140,7 @@ async function sendSkillsPage(chatId, channel, skills2, page) {
16884
17140
  const header3 = totalPages > 1 ? `Skills (page ${safePage}/${totalPages}, ${skills2.length} total):` : "Available skills:";
16885
17141
  const footer = totalPages > 1 ? `
16886
17142
  Use /skills <page> to navigate (e.g. /skills 2)` : "";
16887
- await channel.sendText(chatId, [header3, "", ...lines, footer].join("\n"), "plain");
17143
+ await channel.sendText(chatId, [header3, "", ...lines, footer].join("\n"), { parseMode: "plain" });
16888
17144
  return;
16889
17145
  }
16890
17146
  const buttons = pageSkills.map((s) => {
@@ -16901,7 +17157,7 @@ Use /skills <page> to navigate (e.g. /skills 2)` : "";
16901
17157
  const header2 = totalPages > 1 ? `${skills2.length} skills (page ${safePage}/${totalPages}). Select one to invoke:` : `${skills2.length} skills available. Select one to invoke:`;
16902
17158
  await channel.sendKeyboard(chatId, header2, buttons);
16903
17159
  }
16904
- var PERM_MODES, VERBOSE_LEVELS, HELP_CATEGORIES, USAGE_WINDOW_MAP, MEDIA_INCOMING_PATH, TONE_PATTERNS, pendingInterrupts, bypassBusyCheck, pendingFallbackMessages, dashboardClawWarnings, pendingSummaryUndo, pendingNewchatUndo, CLI_INSTALL_HINTS, BLOCKED_PATH_PATTERNS2, ALLOWED_REACTION_EMOJIS, SKILLS_PER_PAGE;
17160
+ var PERM_MODES, VERBOSE_LEVELS, HELP_CATEGORIES, USAGE_WINDOW_MAP, MEDIA_INCOMING_PATH, TONE_PATTERNS, pendingInterrupts, bypassBusyCheck, activeSideQuests, MAX_SIDE_QUESTS, pendingFallbackMessages, dashboardClawWarnings, pendingSummaryUndo, pendingNewchatUndo, CLI_INSTALL_HINTS, BLOCKED_PATH_PATTERNS2, ALLOWED_REACTION_EMOJIS, SKILLS_PER_PAGE;
16905
17161
  var init_router = __esm({
16906
17162
  "src/router.ts"() {
16907
17163
  "use strict";
@@ -17040,6 +17296,8 @@ var init_router = __esm({
17040
17296
  ];
17041
17297
  pendingInterrupts = /* @__PURE__ */ new Map();
17042
17298
  bypassBusyCheck = /* @__PURE__ */ new Set();
17299
+ activeSideQuests = /* @__PURE__ */ new Map();
17300
+ MAX_SIDE_QUESTS = 2;
17043
17301
  pendingFallbackMessages = /* @__PURE__ */ new Map();
17044
17302
  dashboardClawWarnings = /* @__PURE__ */ new Map();
17045
17303
  pendingSummaryUndo = /* @__PURE__ */ new Map();
@@ -17502,9 +17760,64 @@ Use the CC-Claw CLI when you need to:
17502
17760
 
17503
17761
  ## Telegram Quick Reference
17504
17762
 
17505
- - \`/menu\` (\`/m\`) \u2014 Home screen keyboard with quick access to all major features
17506
- - \`/usage\` \u2014 Unified cost, limits, and usage view (\`/cost\` and \`/limits\` are aliases)
17507
- - \`@botname <query>\` \u2014 Inline mode: memory/history search from any Telegram chat (requires \`/setinline\` in BotFather)
17763
+ **Navigation & help:**
17764
+ - \`/menu\` (\`/m\`) \u2014 Home screen keyboard
17765
+ - \`/help\` (\`/start\`) \u2014 Help categories keyboard
17766
+ - \`/usage\` \u2014 Unified cost, limits, usage (\`/cost\` and \`/limits\` are aliases)
17767
+ - \`@botname <query>\` \u2014 Inline mode: memory/history search from any chat
17768
+
17769
+ **Session & history:**
17770
+ - \`/newchat\` (\`/new\`) \u2014 Start fresh conversation (with undo)
17771
+ - \`/summarize\` \u2014 Save session to memory (\`/summarize all\` for all pending)
17772
+ - \`/history\` \u2014 Recent messages (\`/history <query>\` for FTS search)
17773
+ - \`/stop\` \u2014 Cancel running task + any active side quests
17774
+
17775
+ **Side quests (parallel execution):**
17776
+ - \`sq: <message>\` or \`btw: <message>\` \u2014 Run in parallel while agent is busy (full tool access, shared context, max 2 concurrent, 5-min timeout)
17777
+ - Also available as "Side quest" button on the interrupt keyboard
17778
+
17779
+ **Backend & model:**
17780
+ - \`/backend\` \u2014 Backend picker keyboard
17781
+ - \`/claude\`, \`/gemini\`, \`/codex\`, \`/cursor\` \u2014 Switch directly
17782
+ - \`/model\` \u2014 Model picker keyboard
17783
+ - \`/thinking\` (\`/think\`) \u2014 Thinking level picker
17784
+
17785
+ **Settings keyboards:**
17786
+ - \`/permissions\` \u2014 Permission mode (yolo/safe/readonly/plan)
17787
+ - \`/tools\` \u2014 Enable/disable tools (safe mode)
17788
+ - \`/response_style\` \u2014 Concise / normal / detailed
17789
+ - \`/model_signature\` \u2014 Show/hide model name on responses
17790
+ - \`/agents mode\` \u2014 Agent mode (auto/native/claw)
17791
+ - \`/voice_config\` \u2014 Voice provider settings
17792
+
17793
+ **Memory:**
17794
+ - \`/remember <text>\` \u2014 Save a memory
17795
+ - \`/forget <keyword>\` \u2014 Delete memories
17796
+ - \`/memories\` \u2014 List memories
17797
+
17798
+ **Scheduling (interactive wizards):**
17799
+ - \`/schedule <description>\` \u2014 Create job via wizard
17800
+ - \`/jobs\` \u2014 Paginated job board
17801
+ - \`/editjob\` \u2014 Edit job wizard
17802
+ - \`/cancel\`, \`/pause\`, \`/resume\`, \`/run\`, \`/runs\` \u2014 Job action shortcuts
17803
+ - \`/health\` \u2014 Scheduler health
17804
+
17805
+ **Agents:**
17806
+ - \`/agents\` \u2014 Agent dashboard
17807
+ - \`/agents history\` \u2014 Recent sub-agent activity
17808
+ - \`/tasks\` \u2014 Task board
17809
+ - \`/stopagent\` \u2014 Interactive agent picker to stop
17810
+ - \`/stopall\` \u2014 Cancel all agents
17811
+ - \`/runners\` \u2014 List CLI runners
17812
+
17813
+ **Other:**
17814
+ - \`/imagine <prompt>\` (\`/image\`) \u2014 Generate image (requires GEMINI_API_KEY)
17815
+ - \`/skills\` \u2014 List skills
17816
+ - \`/skill-install <url>\` \u2014 Install skill from GitHub
17817
+ - \`/evolve\` \u2014 Self-learning interactive keyboard
17818
+ - \`/reflect\` \u2014 Trigger reflection analysis
17819
+ - \`/gemini_accounts\` \u2014 Gemini credential rotation management
17820
+ - \`/setup-profile\` \u2014 User profile setup wizard
17508
17821
 
17509
17822
  ## Command Reference
17510
17823
 
@@ -17516,6 +17829,7 @@ cc-claw doctor --json # Health checks (DB, CLIs, env, disk)
17516
17829
  cc-claw doctor --fix --json # Auto-repair common issues
17517
17830
  cc-claw logs -f # Follow daemon logs
17518
17831
  cc-claw logs --error # Show error log
17832
+ cc-claw logs --lines 50 # Show last N lines
17519
17833
  \`\`\`
17520
17834
 
17521
17835
  ### Backend & Model
@@ -17532,8 +17846,10 @@ cc-claw model set claude-opus-4-6 # Switch model
17532
17846
  \`\`\`bash
17533
17847
  cc-claw chat send "What is 2+2?" --json # Send message, get response
17534
17848
  cc-claw chat send "analyze this" --stream # Stream response tokens
17535
- cc-claw chat stop # Cancel running task
17536
- cc-claw tui # Interactive terminal chat
17849
+ cc-claw chat send "msg" --backend gemini --model gemini-3-flash-preview # Override backend/model
17850
+ cc-claw chat send "msg" --thinking high --cwd /path # Override thinking/cwd
17851
+ cc-claw chat stop # Cancel running task + side quests
17852
+ cc-claw tui --model claude-opus-4-6 # Interactive chat with model override
17537
17853
  \`\`\`
17538
17854
 
17539
17855
  ### Memory
@@ -17541,6 +17857,7 @@ cc-claw tui # Interactive terminal chat
17541
17857
  cc-claw memory list --json # All memories with salience
17542
17858
  cc-claw memory search "topic" --json # Search memories
17543
17859
  cc-claw memory history --json # Session summaries
17860
+ cc-claw memory history --limit 20 --json # Limit results
17544
17861
  cc-claw memory add "key" "value" # Save a memory (needs daemon)
17545
17862
  cc-claw memory forget "keyword" # Delete memories (needs daemon)
17546
17863
  \`\`\`
@@ -17555,7 +17872,8 @@ cc-claw session new # Clear session (newchat + summarize)
17555
17872
  \`\`\`bash
17556
17873
  cc-claw cron list --json # All scheduled jobs
17557
17874
  cc-claw cron health --json # Scheduler health
17558
- cc-claw cron runs --json # Run history
17875
+ cc-claw cron runs --json # Run history (all jobs)
17876
+ cc-claw cron runs 3 --limit 10 --json # Run history for job #3
17559
17877
  cc-claw cron create --description "Morning briefing" --cron "0 9 * * *" --backend claude
17560
17878
  cc-claw cron edit 3 --model gemini-3-flash-preview # Change job model
17561
17879
  cc-claw cron edit 3 --backend gemini --model gemini-3-flash-preview # Change backend + model
@@ -17572,6 +17890,7 @@ cc-claw cron run 3 # Trigger immediately
17572
17890
  \`\`\`bash
17573
17891
  cc-claw agents list --json # Active sub-agents
17574
17892
  cc-claw agents spawn --runner claude --task "review code" --json
17893
+ cc-claw agents spawn --runner gemini --task "analyze" --model gemini-3-flash-preview --role worker
17575
17894
  cc-claw agents cancel <id> # Cancel agent
17576
17895
  cc-claw agents cancel-all # Cancel all
17577
17896
  cc-claw tasks list --json # Task board
@@ -17584,6 +17903,8 @@ cc-claw config list --json # All runtime config
17584
17903
  cc-claw config get backend --json # Specific config value
17585
17904
  cc-claw config set backend gemini # Set config (needs daemon)
17586
17905
  cc-claw config env --json # Static .env values (redacted)
17906
+ cc-claw config response-style # Get current response style
17907
+ cc-claw config response-style detailed # Set response style (concise/normal/detailed)
17587
17908
  \`\`\`
17588
17909
 
17589
17910
  ### Usage & Cost
@@ -17656,6 +17977,37 @@ cc-claw summarizer set off # Disable summarization
17656
17977
  cc-claw summarizer set claude:claude-haiku-4-5 # Pin specific backend:model
17657
17978
  \`\`\`
17658
17979
 
17980
+ ### Gemini Credentials
17981
+ \`\`\`bash
17982
+ cc-claw gemini list --json # Show all credential slots (API keys + OAuth accounts)
17983
+ cc-claw gemini add-key --label "work" # Add an API key slot
17984
+ cc-claw gemini add-account --label "personal" # Add OAuth account (browser sign-in)
17985
+ cc-claw gemini remove <id-or-label> # Remove a credential slot
17986
+ cc-claw gemini enable <id-or-label> # Enable a disabled slot
17987
+ cc-claw gemini disable <id-or-label> # Disable a slot (skip in rotation)
17988
+ cc-claw gemini reorder <id-or-label> <priority> # Change rotation priority
17989
+ cc-claw gemini rotation # Get current rotation mode
17990
+ cc-claw gemini rotation all # Set mode (off/all/accounts/keys)
17991
+ \`\`\`
17992
+
17993
+ ### Self-Learning (evolve)
17994
+ \`\`\`bash
17995
+ cc-claw evolve --json # Status overview
17996
+ cc-claw evolve analyze # Trigger reflection analysis
17997
+ cc-claw evolve list --json # Pending proposals
17998
+ cc-claw evolve get <id> --json # Show proposal with diff
17999
+ cc-claw evolve apply <id> # Apply a proposal
18000
+ cc-claw evolve reject <id> # Reject a proposal
18001
+ cc-claw evolve undo <id> # Rollback applied insight
18002
+ cc-claw evolve on # Enable self-learning
18003
+ cc-claw evolve off # Disable self-learning
18004
+ cc-claw evolve model auto # Set analysis model (auto/pinned/cheap)
18005
+ cc-claw evolve model pinned --backend claude --model claude-haiku-4-5
18006
+ cc-claw evolve stats --days 30 --json # Growth report
18007
+ cc-claw evolve history --json # Applied/rejected insight history
18008
+ cc-claw evolve history --status applied --limit 10 # Filter history
18009
+ \`\`\`
18010
+
17659
18011
  ### Chat Aliases
17660
18012
  \`\`\`bash
17661
18013
  cc-claw chats list --json # Authorized chats and aliases
@@ -17678,8 +18030,7 @@ cc-claw mcps list --json # Registered MCP servers
17678
18030
  \`\`\`bash
17679
18031
  cc-claw db stats --json # Row counts, file size, WAL status
17680
18032
  cc-claw db path # Print DB file location
17681
- cc-claw db backup # Create backup
17682
- cc-claw db prune # Prune old data
18033
+ cc-claw db backup [path] # Create backup (optional destination)
17683
18034
  \`\`\`
17684
18035
 
17685
18036
  ### Service Management
@@ -17697,6 +18048,7 @@ cc-claw service uninstall # Remove service
17697
18048
  cc-claw setup # Interactive configuration wizard
17698
18049
  cc-claw tui # Interactive terminal chat
17699
18050
  cc-claw completion --shell zsh # Generate shell completions (bash/zsh/fish)
18051
+ cc-claw completion --shell zsh --install # Show install instructions
17700
18052
  cc-claw --ai # Generate/install SKILL.md for AI tools
17701
18053
  \`\`\`
17702
18054
 
@@ -17873,7 +18225,7 @@ async function main() {
17873
18225
  pendingSummarizeNotify = () => {
17874
18226
  if (primaryChatId) {
17875
18227
  for (const ch of channelRegistry.list()) {
17876
- ch.sendText(primaryChatId, "\u{1F504} Restarted \u2014 your conversations were summarized and saved.", "plain").catch(() => {
18228
+ ch.sendText(primaryChatId, "\u{1F504} Restarted \u2014 your conversations were summarized and saved.", { parseMode: "plain" }).catch(() => {
17877
18229
  });
17878
18230
  }
17879
18231
  }
@@ -17900,7 +18252,7 @@ async function main() {
17900
18252
  setNotifyCallback((chatId, message) => {
17901
18253
  notifyQueue = notifyQueue.then(async () => {
17902
18254
  for (const ch of channelRegistry.list()) {
17903
- await ch.sendText(chatId, `\u{1F916} ${message}`, "plain").catch(() => {
18255
+ await ch.sendText(chatId, `\u{1F916} ${message}`, { parseMode: "plain" }).catch(() => {
17904
18256
  });
17905
18257
  }
17906
18258
  await new Promise((r) => setTimeout(r, 300));