cc-claw 0.18.1 → 0.18.3

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 +558 -75
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -33,7 +33,7 @@ var VERSION;
33
33
  var init_version = __esm({
34
34
  "src/version.ts"() {
35
35
  "use strict";
36
- VERSION = true ? "0.18.1" : (() => {
36
+ VERSION = true ? "0.18.3" : (() => {
37
37
  try {
38
38
  return JSON.parse(readFileSync(join(process.cwd(), "package.json"), "utf-8")).version ?? "unknown";
39
39
  } catch {
@@ -1342,7 +1342,8 @@ function initSchema(db3) {
1342
1342
  created_at TEXT NOT NULL DEFAULT (datetime('now')),
1343
1343
  last_run_at TEXT,
1344
1344
  next_run_at TEXT,
1345
- consecutive_failures INTEGER NOT NULL DEFAULT 0
1345
+ consecutive_failures INTEGER NOT NULL DEFAULT 0,
1346
+ allow_paid_slots INTEGER NOT NULL DEFAULT 0
1346
1347
  );
1347
1348
  `);
1348
1349
  try {
@@ -1498,6 +1499,10 @@ function initSchema(db3) {
1498
1499
  db3.exec("ALTER TABLE jobs ADD COLUMN job_type TEXT DEFAULT 'normal'");
1499
1500
  } catch {
1500
1501
  }
1502
+ try {
1503
+ db3.exec("ALTER TABLE jobs ADD COLUMN allow_paid_slots INTEGER NOT NULL DEFAULT 0");
1504
+ } catch {
1505
+ }
1501
1506
  try {
1502
1507
  db3.exec("UPDATE jobs SET job_type = 'reflection' WHERE description LIKE '%reflection analysis%' AND job_type = 'normal'");
1503
1508
  } catch {
@@ -1781,6 +1786,13 @@ function initSchema(db3) {
1781
1786
  PRIMARY KEY (chat_id, backend)
1782
1787
  );
1783
1788
  `);
1789
+ db3.exec(`
1790
+ CREATE TABLE IF NOT EXISTS chat_allow_paid_slots (
1791
+ chat_id TEXT NOT NULL,
1792
+ backend TEXT NOT NULL,
1793
+ PRIMARY KEY (chat_id, backend)
1794
+ );
1795
+ `);
1784
1796
  try {
1785
1797
  const deleted = db3.prepare(
1786
1798
  "DELETE FROM usage_log WHERE created_at < datetime('now', '-90 days')"
@@ -2445,6 +2457,8 @@ var chat_settings_exports = {};
2445
2457
  __export(chat_settings_exports, {
2446
2458
  ALL_TOOLS: () => ALL_TOOLS,
2447
2459
  clearAgentMode: () => clearAgentMode,
2460
+ clearAllPaidSlots: () => clearAllPaidSlots,
2461
+ clearChatPaidSlots: () => clearChatPaidSlots,
2448
2462
  clearCwd: () => clearCwd,
2449
2463
  clearExecMode: () => clearExecMode,
2450
2464
  clearModel: () => clearModel,
@@ -2455,6 +2469,7 @@ __export(chat_settings_exports, {
2455
2469
  findBookmarksByPrefix: () => findBookmarksByPrefix,
2456
2470
  getAgentMode: () => getAgentMode,
2457
2471
  getAllBookmarks: () => getAllBookmarks,
2472
+ getAllowPaidSlots: () => getAllowPaidSlots,
2458
2473
  getBackend: () => getBackend,
2459
2474
  getBookmark: () => getBookmark,
2460
2475
  getCwd: () => getCwd,
@@ -2473,6 +2488,7 @@ __export(chat_settings_exports, {
2473
2488
  removePendingEscalation: () => removePendingEscalation,
2474
2489
  resetTools: () => resetTools,
2475
2490
  setAgentMode: () => setAgentMode,
2491
+ setAllowPaidSlots: () => setAllowPaidSlots,
2476
2492
  setBackend: () => setBackend,
2477
2493
  setCwd: () => setCwd,
2478
2494
  setExecMode: () => setExecMode,
@@ -2774,6 +2790,23 @@ function toggleSessionLogEnabled(chatId) {
2774
2790
  setSessionLogEnabled(chatId, next);
2775
2791
  return next;
2776
2792
  }
2793
+ function getAllowPaidSlots(chatId, backend2) {
2794
+ const row = getDb().prepare(
2795
+ "SELECT 1 FROM chat_allow_paid_slots WHERE chat_id = ? AND backend = ?"
2796
+ ).get(chatId, backend2);
2797
+ return !!row;
2798
+ }
2799
+ function setAllowPaidSlots(chatId, backend2) {
2800
+ getDb().prepare(`
2801
+ INSERT OR IGNORE INTO chat_allow_paid_slots (chat_id, backend) VALUES (?, ?)
2802
+ `).run(chatId, backend2);
2803
+ }
2804
+ function clearChatPaidSlots(chatId) {
2805
+ getDb().prepare("DELETE FROM chat_allow_paid_slots WHERE chat_id = ?").run(chatId);
2806
+ }
2807
+ function clearAllPaidSlots() {
2808
+ getDb().prepare("DELETE FROM chat_allow_paid_slots").run();
2809
+ }
2777
2810
  var pendingEscalations, ESCALATION_TTL_MS, ALL_TOOLS;
2778
2811
  var init_chat_settings = __esm({
2779
2812
  "src/memory/chat-settings.ts"() {
@@ -3038,8 +3071,8 @@ function insertJob(params) {
3038
3071
  const db3 = getDb();
3039
3072
  const result = db3.prepare(`
3040
3073
  INSERT INTO jobs (schedule_type, cron, at_time, every_ms, title, description, chat_id,
3041
- backend, model, thinking, timeout, fallbacks, session_type, channel, target, delivery_mode, timezone, job_type)
3042
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
3074
+ backend, model, thinking, timeout, fallbacks, session_type, channel, target, delivery_mode, timezone, job_type, allow_paid_slots)
3075
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
3043
3076
  `).run(
3044
3077
  params.scheduleType,
3045
3078
  params.cron ?? null,
@@ -3058,13 +3091,15 @@ function insertJob(params) {
3058
3091
  params.target ?? null,
3059
3092
  params.deliveryMode ?? "announce",
3060
3093
  params.timezone ?? "UTC",
3061
- params.jobType ?? "normal"
3094
+ params.jobType ?? "normal",
3095
+ params.allowPaidSlots ? 1 : 0
3062
3096
  );
3063
3097
  return getJobById(Number(result.lastInsertRowid));
3064
3098
  }
3065
3099
  function mapJobRow(row) {
3066
3100
  if (!row) return void 0;
3067
3101
  row.fallbacks = row.fallbacks ? JSON.parse(row.fallbacks) : [];
3102
+ row.allowPaidSlots = !!row.allowPaidSlots;
3068
3103
  return row;
3069
3104
  }
3070
3105
  function getJobById(id) {
@@ -3118,7 +3153,8 @@ function updateJob(id, fields) {
3118
3153
  target: "target",
3119
3154
  deliveryMode: "delivery_mode",
3120
3155
  timezone: "timezone",
3121
- jobType: "job_type"
3156
+ jobType: "job_type",
3157
+ allowPaidSlots: "allow_paid_slots"
3122
3158
  };
3123
3159
  for (const [key, val] of Object.entries(fields)) {
3124
3160
  const col = fieldMap[key];
@@ -3190,7 +3226,8 @@ var init_jobs = __esm({
3190
3226
  title, description, chat_id as chatId, backend, model, thinking, timeout, fallbacks,
3191
3227
  session_type as sessionType, channel, target, delivery_mode as deliveryMode,
3192
3228
  timezone, job_type as jobType, enabled, active, created_at as createdAt, last_run_at as lastRunAt,
3193
- next_run_at as nextRunAt, consecutive_failures as consecutiveFailures
3229
+ next_run_at as nextRunAt, consecutive_failures as consecutiveFailures,
3230
+ allow_paid_slots as allowPaidSlots
3194
3231
  FROM jobs
3195
3232
  `;
3196
3233
  }
@@ -3370,9 +3407,13 @@ function getGeminiSlots() {
3370
3407
  FROM gemini_credentials ORDER BY priority ASC, id ASC
3371
3408
  `).all();
3372
3409
  }
3373
- function getEligibleGeminiSlots(mode) {
3410
+ function getEligibleGeminiSlots(mode, allowPaid) {
3374
3411
  if (mode === "off") return [];
3375
- const slotTypeFilter = mode === "accounts" ? "AND slot_type = 'oauth'" : mode === "keys" ? "AND slot_type = 'api_key'" : "";
3412
+ const effectiveAllowPaid = mode === "keys" ? true : allowPaid ?? false;
3413
+ const slotTypeFilter = mode === "accounts" ? "AND slot_type = 'oauth'" : mode === "keys" ? "AND slot_type = 'api_key'" : (
3414
+ // "all" or undefined: exclude paid slots unless allowed
3415
+ !effectiveAllowPaid ? "AND slot_type != 'api_key'" : ""
3416
+ );
3376
3417
  return getDb().prepare(`
3377
3418
  SELECT id, slot_type AS slotType, label, api_key AS apiKey, config_home AS configHome,
3378
3419
  priority, enabled, cooldown_until AS cooldownUntil, last_used AS lastUsed,
@@ -3380,7 +3421,8 @@ function getEligibleGeminiSlots(mode) {
3380
3421
  FROM gemini_credentials
3381
3422
  WHERE enabled = 1 AND (cooldown_until IS NULL OR cooldown_until <= datetime('now'))
3382
3423
  ${slotTypeFilter}
3383
- ORDER BY priority ASC, last_used ASC NULLS FIRST
3424
+ ORDER BY CASE WHEN slot_type = 'api_key' THEN 1 ELSE 0 END ASC,
3425
+ priority ASC, last_used ASC NULLS FIRST
3384
3426
  `).all();
3385
3427
  }
3386
3428
  function getChatGeminiSlotId(chatId) {
@@ -3473,13 +3515,20 @@ function getBackendSlots(backend2) {
3473
3515
  FROM backend_credentials bc WHERE bc.backend = ? ORDER BY bc.priority ASC, bc.id ASC
3474
3516
  `).all(backend2);
3475
3517
  }
3476
- function getEligibleBackendSlots(backend2) {
3518
+ function getEligibleBackendSlots(backend2, mode, allowPaid) {
3519
+ if (mode === "off") return [];
3520
+ const slotTypeFilter = mode === "accounts" ? "AND bc.slot_type = 'oauth'" : mode === "keys" ? "AND bc.slot_type = 'api_key'" : (
3521
+ // "all" or undefined: exclude paid slots unless allowed
3522
+ !allowPaid ? "AND bc.slot_type != 'api_key'" : ""
3523
+ );
3477
3524
  return getDb().prepare(`
3478
3525
  SELECT ${SLOT_COLUMNS}
3479
3526
  FROM backend_credentials bc
3480
3527
  WHERE bc.backend = ? AND bc.enabled = 1
3481
3528
  AND (bc.cooldown_until IS NULL OR bc.cooldown_until <= datetime('now'))
3482
- ORDER BY bc.priority ASC, bc.last_used ASC NULLS FIRST
3529
+ ${slotTypeFilter}
3530
+ ORDER BY CASE WHEN bc.slot_type = 'api_key' THEN 1 ELSE 0 END ASC,
3531
+ bc.priority ASC, bc.last_used ASC NULLS FIRST
3483
3532
  `).all(backend2);
3484
3533
  }
3485
3534
  function getChatBackendSlotId(chatId, backend2) {
@@ -3550,6 +3599,13 @@ function reenableBackendSlot(slotId) {
3550
3599
  WHERE id = ?
3551
3600
  `).run(slotId);
3552
3601
  }
3602
+ function getBackendRotationMode(backend2) {
3603
+ const row = getDb().prepare("SELECT value FROM meta WHERE key = ?").get(`${backend2}_rotation_mode`);
3604
+ return row?.value ?? "all";
3605
+ }
3606
+ function setBackendRotationMode(backend2, mode) {
3607
+ getDb().prepare("INSERT OR REPLACE INTO meta (key, value) VALUES (?, ?)").run(`${backend2}_rotation_mode`, mode);
3608
+ }
3553
3609
  var SLOT_COLUMNS;
3554
3610
  var init_backend_slots = __esm({
3555
3611
  "src/memory/backend-slots.ts"() {
@@ -3576,10 +3632,12 @@ __export(store_exports5, {
3576
3632
  checkBackendLimits: () => checkBackendLimits,
3577
3633
  cleanExpiredWatches: () => cleanExpiredWatches,
3578
3634
  clearAgentMode: () => clearAgentMode,
3635
+ clearAllPaidSlots: () => clearAllPaidSlots,
3579
3636
  clearAllSessions: () => clearAllSessions,
3580
3637
  clearBackendLimit: () => clearBackendLimit,
3581
3638
  clearChatBackendSlot: () => clearChatBackendSlot,
3582
3639
  clearChatGeminiSlot: () => clearChatGeminiSlot,
3640
+ clearChatPaidSlots: () => clearChatPaidSlots,
3583
3641
  clearCwd: () => clearCwd,
3584
3642
  clearExecMode: () => clearExecMode,
3585
3643
  clearMessageLog: () => clearMessageLog,
@@ -3605,8 +3663,10 @@ __export(store_exports5, {
3605
3663
  getAllMemoriesWithEmbeddings: () => getAllMemoriesWithEmbeddings,
3606
3664
  getAllSessionSummariesWithEmbeddings: () => getAllSessionSummariesWithEmbeddings,
3607
3665
  getAllTimeUsage: () => getAllTimeUsage,
3666
+ getAllowPaidSlots: () => getAllowPaidSlots,
3608
3667
  getBackend: () => getBackend,
3609
3668
  getBackendLimit: () => getBackendLimit,
3669
+ getBackendRotationMode: () => getBackendRotationMode,
3610
3670
  getBackendSlots: () => getBackendSlots,
3611
3671
  getBackendUsageInWindow: () => getBackendUsageInWindow,
3612
3672
  getBookmark: () => getBookmark,
@@ -3686,8 +3746,10 @@ __export(store_exports5, {
3686
3746
  searchMessageLog: () => searchMessageLog,
3687
3747
  searchSessionSummaries: () => searchSessionSummaries,
3688
3748
  setAgentMode: () => setAgentMode,
3749
+ setAllowPaidSlots: () => setAllowPaidSlots,
3689
3750
  setBackend: () => setBackend,
3690
3751
  setBackendLimit: () => setBackendLimit,
3752
+ setBackendRotationMode: () => setBackendRotationMode,
3691
3753
  setBackendSlotEnabled: () => setBackendSlotEnabled,
3692
3754
  setBootTime: () => setBootTime,
3693
3755
  setChatAlias: () => setChatAlias,
@@ -9655,6 +9717,14 @@ function parseAnalysisOutput(raw) {
9655
9717
  if (!VALID_CATEGORIES.includes(category)) continue;
9656
9718
  const confidence = confidenceMatch ? parseFloat(confidenceMatch[1].trim()) : 0.5;
9657
9719
  if (isNaN(confidence)) continue;
9720
+ let proposedDiff = diffMatch?.[1]?.trim() ?? "";
9721
+ if (proposedDiff.startsWith("```")) {
9722
+ proposedDiff = proposedDiff.replace(/^```[a-zA-Z0-9-]*\r?\n/, "");
9723
+ if (proposedDiff.endsWith("```")) {
9724
+ proposedDiff = proposedDiff.replace(/\r?\n```$/, "");
9725
+ }
9726
+ proposedDiff = proposedDiff.trim();
9727
+ }
9658
9728
  results.push({
9659
9729
  insight: insightMatch[1].trim(),
9660
9730
  category,
@@ -9662,7 +9732,7 @@ function parseAnalysisOutput(raw) {
9662
9732
  targetFile: targetMatch?.[1]?.trim() ?? "",
9663
9733
  confidence: Math.max(0, Math.min(1, confidence)),
9664
9734
  proposedAction: actionMatch?.[1]?.trim() ?? "",
9665
- proposedDiff: diffMatch?.[1]?.trim() ?? "",
9735
+ proposedDiff,
9666
9736
  conflictsWith: conflictsMatch?.[1]?.trim() ?? "none"
9667
9737
  });
9668
9738
  }
@@ -10066,6 +10136,181 @@ var init_analyze = __esm({
10066
10136
  }
10067
10137
  });
10068
10138
 
10139
+ // src/reflection/ai-apply.ts
10140
+ var ai_apply_exports = {};
10141
+ __export(ai_apply_exports, {
10142
+ applyWithAI: () => applyWithAI
10143
+ });
10144
+ import { spawn as spawn5 } from "child_process";
10145
+ import { createInterface as createInterface4 } from "readline";
10146
+ function buildApplyPrompt(originalContent, instruction, proposedDiff, targetFile) {
10147
+ return `You are a precise markdown document editor. Your ONLY job is to apply the requested change to the document while preserving its structural integrity.
10148
+
10149
+ === CURRENT FILE: ${targetFile} ===
10150
+ ${originalContent}
10151
+ === END FILE ===
10152
+
10153
+ === CHANGE TO APPLY ===
10154
+ Instruction: ${instruction}
10155
+
10156
+ Diff guidance:
10157
+ ${proposedDiff}
10158
+ === END CHANGE ===
10159
+
10160
+ Rules:
10161
+ 1. Place new content under the semantically correct section heading (## heading)
10162
+ 2. Maintain consistent formatting (bullet style, indentation) with the target section
10163
+ 3. Do NOT add, remove, or modify any content other than what the change requires
10164
+ 4. Do NOT wrap your output in markdown code fences
10165
+ 5. Do NOT add explanations, commentary, or notes
10166
+ 6. Preserve all existing headings and their order
10167
+ 7. Return ONLY the complete updated file content
10168
+
10169
+ Output the complete updated file now:`;
10170
+ }
10171
+ async function spawnApplyLLM(adapter, model2, prompt) {
10172
+ const config2 = adapter.buildSpawnConfig({
10173
+ prompt,
10174
+ model: model2,
10175
+ permMode: "yolo",
10176
+ allowedTools: []
10177
+ });
10178
+ const env = adapter.getEnv();
10179
+ let resultText = "";
10180
+ let accumulatedText = "";
10181
+ await new Promise((resolve) => {
10182
+ const proc = spawn5(config2.executable, config2.args, {
10183
+ env,
10184
+ stdio: ["ignore", "pipe", "pipe"],
10185
+ ...config2.cwd ? { cwd: config2.cwd } : {}
10186
+ });
10187
+ proc.stderr?.resume();
10188
+ const rl2 = createInterface4({ input: proc.stdout });
10189
+ const timeout = setTimeout(() => {
10190
+ warn("[ai-apply] LLM spawn timeout");
10191
+ rl2.close();
10192
+ proc.kill("SIGTERM");
10193
+ setTimeout(() => proc.kill("SIGKILL"), 2e3);
10194
+ }, AI_APPLY_TIMEOUT_MS);
10195
+ rl2.on("line", (line) => {
10196
+ if (!line.trim()) return;
10197
+ let msg;
10198
+ try {
10199
+ msg = JSON.parse(line);
10200
+ } catch {
10201
+ return;
10202
+ }
10203
+ const events = adapter.parseLine(msg);
10204
+ for (const ev of events) {
10205
+ if (ev.type === "text" && ev.text) accumulatedText = appendTextChunk(accumulatedText, ev.text);
10206
+ if (ev.type === "result") {
10207
+ resultText = ev.resultText || accumulatedText;
10208
+ if (adapter.shouldKillOnResult()) {
10209
+ rl2.close();
10210
+ proc.kill("SIGTERM");
10211
+ }
10212
+ }
10213
+ }
10214
+ });
10215
+ proc.on("error", () => {
10216
+ clearTimeout(timeout);
10217
+ resolve();
10218
+ });
10219
+ proc.on("close", () => {
10220
+ clearTimeout(timeout);
10221
+ resolve();
10222
+ });
10223
+ });
10224
+ if (!resultText) resultText = accumulatedText;
10225
+ return resultText;
10226
+ }
10227
+ function resolveApplyAdapter(chatId) {
10228
+ try {
10229
+ const adapter = getAdapterForChat(chatId);
10230
+ return { adapter, model: adapter.defaultModel };
10231
+ } catch {
10232
+ try {
10233
+ const adapter = getAdapter("claude");
10234
+ return { adapter, model: adapter.defaultModel };
10235
+ } catch {
10236
+ return null;
10237
+ }
10238
+ }
10239
+ }
10240
+ function extractHeadings(content) {
10241
+ return content.split("\n").filter((line) => /^#{1,6}\s/.test(line)).map((line) => line.trim());
10242
+ }
10243
+ function validateOutput(original, output2) {
10244
+ if (!output2.trim()) {
10245
+ return { valid: false, reason: "LLM returned empty output" };
10246
+ }
10247
+ const originalLines = original.split("\n").length;
10248
+ const outputLines = output2.split("\n").length;
10249
+ if (originalLines > 5 && outputLines < originalLines * MIN_LINE_RETENTION) {
10250
+ return {
10251
+ valid: false,
10252
+ reason: `Output has ${outputLines} lines vs original ${originalLines} (${Math.round(outputLines / originalLines * 100)}% retained, min ${MIN_LINE_RETENTION * 100}%)`
10253
+ };
10254
+ }
10255
+ const originalHeadings = extractHeadings(original);
10256
+ const outputHeadings = extractHeadings(output2);
10257
+ for (const heading4 of originalHeadings) {
10258
+ if (!outputHeadings.includes(heading4)) {
10259
+ return {
10260
+ valid: false,
10261
+ reason: `Missing heading: "${heading4}"`
10262
+ };
10263
+ }
10264
+ }
10265
+ if (output2.trim() === original.trim()) {
10266
+ return { valid: false, reason: "Output is identical to original (no changes applied)" };
10267
+ }
10268
+ return { valid: true };
10269
+ }
10270
+ function stripMarkdownFences(text) {
10271
+ const fencePattern = /^```(?:markdown|md)?\s*\n([\s\S]*?)\n```\s*$/;
10272
+ const match = text.match(fencePattern);
10273
+ if (match) return match[1];
10274
+ return text;
10275
+ }
10276
+ async function applyWithAI(chatId, originalContent, instruction, proposedDiff, targetFile) {
10277
+ const resolved = resolveApplyAdapter(chatId);
10278
+ if (!resolved) {
10279
+ return { success: false, newContent: "", error: "No backend adapter available" };
10280
+ }
10281
+ const { adapter, model: model2 } = resolved;
10282
+ log(`[ai-apply] Using ${adapter.id}:${model2} for structural apply on ${targetFile}`);
10283
+ try {
10284
+ const prompt = buildApplyPrompt(originalContent, instruction, proposedDiff, targetFile);
10285
+ const rawOutput = await spawnApplyLLM(adapter, model2, prompt);
10286
+ if (!rawOutput.trim()) {
10287
+ return { success: false, newContent: "", error: "LLM returned empty response" };
10288
+ }
10289
+ const cleanOutput = stripMarkdownFences(rawOutput);
10290
+ const validation = validateOutput(originalContent, cleanOutput);
10291
+ if (!validation.valid) {
10292
+ return { success: false, newContent: "", error: `Validation failed: ${validation.reason}` };
10293
+ }
10294
+ log(`[ai-apply] Successfully applied change to ${targetFile}`);
10295
+ return { success: true, newContent: cleanOutput };
10296
+ } catch (err) {
10297
+ const msg = err instanceof Error ? err.message : String(err);
10298
+ warn(`[ai-apply] LLM apply failed: ${msg}`);
10299
+ return { success: false, newContent: "", error: msg };
10300
+ }
10301
+ }
10302
+ var AI_APPLY_TIMEOUT_MS, MIN_LINE_RETENTION;
10303
+ var init_ai_apply = __esm({
10304
+ "src/reflection/ai-apply.ts"() {
10305
+ "use strict";
10306
+ init_log();
10307
+ init_text_utils();
10308
+ init_backends();
10309
+ AI_APPLY_TIMEOUT_MS = 6e4;
10310
+ MIN_LINE_RETENTION = 0.7;
10311
+ }
10312
+ });
10313
+
10069
10314
  // src/reflection/apply.ts
10070
10315
  var apply_exports = {};
10071
10316
  __export(apply_exports, {
@@ -10196,7 +10441,31 @@ async function applyInsight(insightId) {
10196
10441
  writeFileSync5(backupPath, original, "utf-8");
10197
10442
  pruneBackups(absolutePath);
10198
10443
  }
10199
- const newContent = applyDiff(original, insight.proposedDiff, insight.proposedAction);
10444
+ let newContent;
10445
+ if (insight.proposedAction !== "create") {
10446
+ try {
10447
+ const { applyWithAI: applyWithAI2 } = await Promise.resolve().then(() => (init_ai_apply(), ai_apply_exports));
10448
+ const aiResult = await applyWithAI2(
10449
+ chatId,
10450
+ original,
10451
+ insight.insight,
10452
+ insight.proposedDiff,
10453
+ insight.targetFile
10454
+ );
10455
+ if (aiResult.success) {
10456
+ newContent = aiResult.newContent;
10457
+ log(`[reflection/apply] AI apply succeeded for insight #${insightId}`);
10458
+ } else {
10459
+ warn(`[reflection/apply] AI apply failed (${aiResult.error}), falling back to static diff`);
10460
+ newContent = applyDiff(original, insight.proposedDiff, insight.proposedAction);
10461
+ }
10462
+ } catch (aiErr) {
10463
+ warn(`[reflection/apply] AI apply threw (${aiErr}), falling back to static diff`);
10464
+ newContent = applyDiff(original, insight.proposedDiff, insight.proposedAction);
10465
+ }
10466
+ } else {
10467
+ newContent = applyDiff(original, insight.proposedDiff, insight.proposedAction);
10468
+ }
10200
10469
  writeFileSync5(absolutePath, newContent, "utf-8");
10201
10470
  const rollbackData = JSON.stringify({ original, backupPath, appliedAt: (/* @__PURE__ */ new Date()).toISOString() });
10202
10471
  updateInsightRollback(db3, insightId, rollbackData);
@@ -10886,6 +11155,7 @@ var init_detect = __esm({
10886
11155
  var agent_exports = {};
10887
11156
  __export(agent_exports, {
10888
11157
  FIRST_RESPONSE_TIMEOUT_ERROR: () => FIRST_RESPONSE_TIMEOUT_ERROR,
11158
+ FREE_SLOTS_EXHAUSTED: () => FREE_SLOTS_EXHAUSTED,
10889
11159
  askAgent: () => askAgent,
10890
11160
  getInFlightMessage: () => getInFlightMessage,
10891
11161
  isChatBusy: () => isChatBusy,
@@ -10893,8 +11163,8 @@ __export(agent_exports, {
10893
11163
  stopAgent: () => stopAgent,
10894
11164
  stopAllActiveAgents: () => stopAllActiveAgents
10895
11165
  });
10896
- import { spawn as spawn5 } from "child_process";
10897
- import { createInterface as createInterface4 } from "readline";
11166
+ import { spawn as spawn6 } from "child_process";
11167
+ import { createInterface as createInterface5 } from "readline";
10898
11168
  function isSyntheticChatId(chatId) {
10899
11169
  return chatId.startsWith("sq:") || chatId.startsWith("cron:");
10900
11170
  }
@@ -10958,7 +11228,7 @@ function spawnQuery(adapter, config2, model2, cancelState, thinkingLevel, timeou
10958
11228
  const env = opts?.envOverride ? thinkingConfig?.envOverrides ? { ...opts.envOverride, ...thinkingConfig.envOverrides } : opts.envOverride : adapter.getEnv(thinkingConfig?.envOverrides);
10959
11229
  const finalArgs = thinkingConfig?.extraArgs ? [...config2.args, ...thinkingConfig.extraArgs] : config2.args;
10960
11230
  log(`[agent:spawn] backend=${adapter.id} exe=${config2.executable} model=${model2} timeout=${effectiveTimeout / 1e3}s cwd=${config2.cwd ?? "(inherited)"}`);
10961
- const proc = spawn5(config2.executable, finalArgs, {
11231
+ const proc = spawn6(config2.executable, finalArgs, {
10962
11232
  env,
10963
11233
  stdio: ["ignore", "pipe", "pipe"],
10964
11234
  detached: true,
@@ -10991,7 +11261,7 @@ function spawnQuery(adapter, config2, model2, cancelState, thinkingLevel, timeou
10991
11261
  const pendingTools = /* @__PURE__ */ new Map();
10992
11262
  const stderrChunks = [];
10993
11263
  proc.stderr?.on("data", (chunk) => stderrChunks.push(chunk));
10994
- const rl2 = createInterface4({ input: proc.stdout });
11264
+ const rl2 = createInterface5({ input: proc.stdout });
10995
11265
  let firstLine = true;
10996
11266
  const frTimeoutMs = adapter.id === "gemini" ? opts?.firstResponseTimeoutMs ?? FIRST_RESPONSE_TIMEOUT_MS : 0;
10997
11267
  let firstResponseTimer;
@@ -11200,9 +11470,15 @@ Partial output: ${accumulatedText.slice(-500)}`;
11200
11470
  });
11201
11471
  });
11202
11472
  }
11203
- async function spawnWithSlotRotation(chatId, adapter, baseConfig, configWithSession, model2, cancelState, thinkingLevel, timeoutMs, maxTurns, opts, onSlotRotation) {
11204
- const slots = getEligibleBackendSlots(adapter.id);
11473
+ async function spawnWithSlotRotation(chatId, adapter, baseConfig, configWithSession, model2, cancelState, thinkingLevel, timeoutMs, maxTurns, rotationMode, allowPaid, opts, onSlotRotation) {
11474
+ const effectiveAllowPaid = rotationMode === "keys" ? true : allowPaid;
11475
+ const slots = getEligibleBackendSlots(adapter.id, rotationMode, effectiveAllowPaid);
11205
11476
  if (slots.length === 0) {
11477
+ const allSlots = getBackendSlots(adapter.id).filter((s) => s.enabled);
11478
+ const paidSlots = allSlots.filter((s) => s.slotType === "api_key");
11479
+ if (!effectiveAllowPaid && paidSlots.length > 0) {
11480
+ throw new Error(`${FREE_SLOTS_EXHAUSTED}|${adapter.id}|${paidSlots.length}`);
11481
+ }
11206
11482
  return spawnQuery(adapter, configWithSession, model2, cancelState, thinkingLevel, timeoutMs, maxTurns, opts);
11207
11483
  }
11208
11484
  const maxAttempts = slots.length;
@@ -11234,7 +11510,7 @@ async function spawnWithSlotRotation(chatId, adapter, baseConfig, configWithSess
11234
11510
  clearSession(chatId);
11235
11511
  } catch {
11236
11512
  }
11237
- const nextSlot = getEligibleBackendSlots(adapter.id)[0];
11513
+ const nextSlot = getEligibleBackendSlots(adapter.id, rotationMode, effectiveAllowPaid)[0];
11238
11514
  if (nextSlot && onSlotRotation) {
11239
11515
  const nextLabel = nextSlot.label || `slot-${nextSlot.id}`;
11240
11516
  onSlotRotation(slotLabel, nextLabel);
@@ -11245,11 +11521,18 @@ async function spawnWithSlotRotation(chatId, adapter, baseConfig, configWithSess
11245
11521
  throw err;
11246
11522
  }
11247
11523
  }
11524
+ if (!effectiveAllowPaid) {
11525
+ const paidSlots = getBackendSlots(adapter.id).filter((s) => s.enabled && s.slotType === "api_key");
11526
+ if (paidSlots.length > 0) {
11527
+ throw new Error(`${FREE_SLOTS_EXHAUSTED}|${adapter.id}|${paidSlots.length}`);
11528
+ }
11529
+ }
11248
11530
  throw lastError ?? new Error(`All ${adapter.id} credential slots exhausted`);
11249
11531
  }
11250
11532
  async function spawnGeminiWithRotation(chatId, adapter, baseConfig, configWithSession, model2, cancelState, thinkingLevel, timeoutMs, maxTurns, rotationMode, opts, onSlotRotation, parentChatId, onModelDowngrade) {
11251
11533
  const geminiAdapter = adapter;
11252
- const slots = getEligibleGeminiSlots(rotationMode);
11534
+ const effectiveAllowPaid = rotationMode === "keys" ? true : getAllowPaidSlots(chatId, "gemini");
11535
+ const slots = getEligibleGeminiSlots(rotationMode, effectiveAllowPaid);
11253
11536
  if (slots.length === 0) {
11254
11537
  return spawnQuery(adapter, configWithSession, model2, cancelState, thinkingLevel, timeoutMs, maxTurns, opts);
11255
11538
  }
@@ -11328,7 +11611,7 @@ async function spawnGeminiWithRotation(chatId, adapter, baseConfig, configWithSe
11328
11611
  clearSession(chatId);
11329
11612
  } catch {
11330
11613
  }
11331
- const nextSlot = getEligibleGeminiSlots(rotationMode)[0];
11614
+ const nextSlot = getEligibleGeminiSlots(rotationMode, effectiveAllowPaid)[0];
11332
11615
  if (nextSlot && onSlotRotation) {
11333
11616
  const nextLabel = nextSlot.label || `slot-${nextSlot.id}`;
11334
11617
  onSlotRotation(slotLabel, nextLabel);
@@ -11413,17 +11696,19 @@ async function askAgentImpl(chatId, userMessage, opts) {
11413
11696
  };
11414
11697
  const resolvedModel = model2 ?? adapter.defaultModel;
11415
11698
  let result = { resultText: "", sessionId: void 0, input: 0, output: 0, cacheRead: 0, sawToolEvents: false, sawResultEvent: false };
11416
- const rotationMode = adapter.id === "gemini" ? getGeminiRotationMode() : "off";
11417
- const useGeminiRotation = rotationMode !== "off" && getEligibleGeminiSlots(rotationMode).length > 0;
11418
- const useBackendRotation = adapter.id !== "gemini" && getEligibleBackendSlots(adapter.id).length > 0;
11699
+ const geminiRotationMode = adapter.id === "gemini" ? getGeminiRotationMode() : "off";
11700
+ const backendRotationMode = adapter.id !== "gemini" ? getBackendRotationMode(adapter.id) : "off";
11701
+ const allowPaid = getAllowPaidSlots(chatId, adapter.id);
11702
+ const useGeminiRotation = geminiRotationMode !== "off" && getEligibleGeminiSlots(geminiRotationMode, allowPaid).length > 0;
11703
+ const useBackendRotation = adapter.id !== "gemini" && (backendRotationMode !== "off" || getBackendSlots(adapter.id).filter((s) => s.enabled).length > 0);
11419
11704
  try {
11420
11705
  if (useGeminiRotation) {
11421
11706
  const rotationCb = onSlotRotation ? (from, to) => onSlotRotation(chatId, from, to) : void 0;
11422
11707
  const downgradeCb = onModelDowngrade ? (from, to, reason) => onModelDowngrade(chatId, from, to, reason) : void 0;
11423
- result = await spawnGeminiWithRotation(chatId, adapter, baseConfig, configWithSession, resolvedModel, cancelState, thinkingLevel, timeoutMs, maxTurns, rotationMode, spawnOpts, rotationCb, settingsSourceChatId, downgradeCb);
11708
+ result = await spawnGeminiWithRotation(chatId, adapter, baseConfig, configWithSession, resolvedModel, cancelState, thinkingLevel, timeoutMs, maxTurns, geminiRotationMode, spawnOpts, rotationCb, settingsSourceChatId, downgradeCb);
11424
11709
  } else if (useBackendRotation) {
11425
11710
  const rotationCb = onSlotRotation ? (from, to) => onSlotRotation(chatId, from, to) : void 0;
11426
- result = await spawnWithSlotRotation(chatId, adapter, baseConfig, configWithSession, resolvedModel, cancelState, thinkingLevel, timeoutMs, maxTurns, spawnOpts, rotationCb);
11711
+ result = await spawnWithSlotRotation(chatId, adapter, baseConfig, configWithSession, resolvedModel, cancelState, thinkingLevel, timeoutMs, maxTurns, backendRotationMode, allowPaid, spawnOpts, rotationCb);
11427
11712
  } else {
11428
11713
  const slotSpawnOpts = (() => {
11429
11714
  if (adapter.id !== "gemini") return spawnOpts;
@@ -11474,10 +11759,10 @@ async function askAgentImpl(chatId, userMessage, opts) {
11474
11759
  if (useGeminiRotation) {
11475
11760
  const rotationCb = onSlotRotation ? (from, to) => onSlotRotation(chatId, from, to) : void 0;
11476
11761
  const downgradeCb = onModelDowngrade ? (from, to, reason) => onModelDowngrade(chatId, from, to, reason) : void 0;
11477
- result = await spawnGeminiWithRotation(chatId, adapter, baseConfig, baseConfig, resolvedModel, cancelState, thinkingLevel, timeoutMs, maxTurns, rotationMode, spawnOpts, rotationCb, settingsSourceChatId, downgradeCb);
11762
+ result = await spawnGeminiWithRotation(chatId, adapter, baseConfig, baseConfig, resolvedModel, cancelState, thinkingLevel, timeoutMs, maxTurns, geminiRotationMode, spawnOpts, rotationCb, settingsSourceChatId, downgradeCb);
11478
11763
  } else if (useBackendRotation) {
11479
11764
  const rotationCb = onSlotRotation ? (from, to) => onSlotRotation(chatId, from, to) : void 0;
11480
- result = await spawnWithSlotRotation(chatId, adapter, baseConfig, baseConfig, resolvedModel, cancelState, thinkingLevel, timeoutMs, maxTurns, spawnOpts, rotationCb);
11765
+ result = await spawnWithSlotRotation(chatId, adapter, baseConfig, baseConfig, resolvedModel, cancelState, thinkingLevel, timeoutMs, maxTurns, backendRotationMode, allowPaid, spawnOpts, rotationCb);
11481
11766
  } else {
11482
11767
  const retryOpts = (() => {
11483
11768
  if (adapter.id !== "gemini") return spawnOpts;
@@ -11497,10 +11782,10 @@ async function askAgentImpl(chatId, userMessage, opts) {
11497
11782
  if (useGeminiRotation) {
11498
11783
  const rotationCb = onSlotRotation ? (from, to) => onSlotRotation(chatId, from, to) : void 0;
11499
11784
  const downgradeCb = onModelDowngrade ? (from, to, reason) => onModelDowngrade(chatId, from, to, reason) : void 0;
11500
- result = await spawnGeminiWithRotation(chatId, adapter, baseConfig, baseConfig, resolvedModel, cancelState, thinkingLevel, timeoutMs, maxTurns, rotationMode, spawnOpts, rotationCb, settingsSourceChatId, downgradeCb);
11785
+ result = await spawnGeminiWithRotation(chatId, adapter, baseConfig, baseConfig, resolvedModel, cancelState, thinkingLevel, timeoutMs, maxTurns, geminiRotationMode, spawnOpts, rotationCb, settingsSourceChatId, downgradeCb);
11501
11786
  } else if (useBackendRotation) {
11502
11787
  const rotationCb = onSlotRotation ? (from, to) => onSlotRotation(chatId, from, to) : void 0;
11503
- result = await spawnWithSlotRotation(chatId, adapter, baseConfig, baseConfig, resolvedModel, cancelState, thinkingLevel, timeoutMs, maxTurns, spawnOpts, rotationCb);
11788
+ result = await spawnWithSlotRotation(chatId, adapter, baseConfig, baseConfig, resolvedModel, cancelState, thinkingLevel, timeoutMs, maxTurns, backendRotationMode, allowPaid, spawnOpts, rotationCb);
11504
11789
  } else {
11505
11790
  const retryOpts = (() => {
11506
11791
  if (adapter.id !== "gemini") return spawnOpts;
@@ -11615,7 +11900,7 @@ function injectMcpConfig(adapterId, args, mcpConfigPath) {
11615
11900
  if (!flag) return args;
11616
11901
  return [...args, ...flag, mcpConfigPath];
11617
11902
  }
11618
- var activeChats, chatLocks, SPAWN_TIMEOUT_MS, FIRST_RESPONSE_TIMEOUT_MS, FIRST_RESPONSE_TIMEOUT_ERROR, GEMINI_FALLBACK_CHAIN, GEMINI_DOWNGRADE_MODELS, MCP_CONFIG_FLAG;
11903
+ var activeChats, chatLocks, SPAWN_TIMEOUT_MS, FIRST_RESPONSE_TIMEOUT_MS, FIRST_RESPONSE_TIMEOUT_ERROR, FREE_SLOTS_EXHAUSTED, GEMINI_FALLBACK_CHAIN, GEMINI_DOWNGRADE_MODELS, MCP_CONFIG_FLAG;
11619
11904
  var init_agent = __esm({
11620
11905
  "src/agent.ts"() {
11621
11906
  "use strict";
@@ -11641,6 +11926,7 @@ var init_agent = __esm({
11641
11926
  SPAWN_TIMEOUT_MS = 10 * 60 * 1e3;
11642
11927
  FIRST_RESPONSE_TIMEOUT_MS = parseInt(process.env.GEMINI_FIRST_RESPONSE_TIMEOUT_MS ?? "30000", 10);
11643
11928
  FIRST_RESPONSE_TIMEOUT_ERROR = "FIRST_RESPONSE_TIMEOUT";
11929
+ FREE_SLOTS_EXHAUSTED = "FREE_SLOTS_EXHAUSTED";
11644
11930
  GEMINI_FALLBACK_CHAIN = {
11645
11931
  "gemini-3.1-pro-preview": "gemini-2.5-pro",
11646
11932
  "gemini-2.5-pro": "gemini-3.1-flash-preview"
@@ -14004,6 +14290,17 @@ var init_image_gen = __esm({
14004
14290
  });
14005
14291
 
14006
14292
  // src/router/response.ts
14293
+ var response_exports = {};
14294
+ __export(response_exports, {
14295
+ ensureReaction: () => ensureReaction,
14296
+ handleResponseExhaustion: () => handleResponseExhaustion,
14297
+ makeToolActionCallback: () => makeToolActionCallback,
14298
+ pendingFallbackMessages: () => pendingFallbackMessages,
14299
+ processFileSends: () => processFileSends2,
14300
+ processImageGenerations: () => processImageGenerations,
14301
+ processReaction: () => processReaction,
14302
+ sendResponse: () => sendResponse
14303
+ });
14007
14304
  import { readFile as readFile3 } from "fs/promises";
14008
14305
  function makeToolActionCallback(chatId, channel, level) {
14009
14306
  return async (toolName, input, result) => {
@@ -16934,8 +17231,8 @@ __export(analyze_exports2, {
16934
17231
  runIdentityAudit: () => runIdentityAudit,
16935
17232
  runSkillAudit: () => runSkillAudit
16936
17233
  });
16937
- import { spawn as spawn6 } from "child_process";
16938
- import { createInterface as createInterface5 } from "readline";
17234
+ import { spawn as spawn7 } from "child_process";
17235
+ import { createInterface as createInterface6 } from "readline";
16939
17236
  import { readFileSync as readFileSync12, existsSync as existsSync21, readdirSync as readdirSync11 } from "fs";
16940
17237
  import { join as join22 } from "path";
16941
17238
  import { homedir as homedir7 } from "os";
@@ -16957,6 +17254,14 @@ function parseOptimizeOutput(raw, validAreas) {
16957
17254
  if (!validAreas.includes(area)) continue;
16958
17255
  const severity = severityMatch?.[1]?.trim().toLowerCase() ?? "info";
16959
17256
  if (!VALID_SEVERITIES.includes(severity)) continue;
17257
+ let proposedDiff = diffMatch?.[1]?.trim() ?? "";
17258
+ if (proposedDiff.startsWith("```")) {
17259
+ proposedDiff = proposedDiff.replace(/^```[a-zA-Z0-9-]*\r?\n/, "");
17260
+ if (proposedDiff.endsWith("```")) {
17261
+ proposedDiff = proposedDiff.replace(/\r?\n```$/, "");
17262
+ }
17263
+ proposedDiff = proposedDiff.trim();
17264
+ }
16960
17265
  results.push({
16961
17266
  title: findingMatch[1].trim().slice(0, 80),
16962
17267
  area,
@@ -16964,7 +17269,7 @@ function parseOptimizeOutput(raw, validAreas) {
16964
17269
  detail: detailMatch?.[1]?.trim() ?? "",
16965
17270
  location: locationMatch?.[1]?.trim() ?? "",
16966
17271
  suggestion: suggestionMatch?.[1]?.trim() ?? "",
16967
- proposedDiff: diffMatch?.[1]?.trim() ?? ""
17272
+ proposedDiff
16968
17273
  });
16969
17274
  }
16970
17275
  return results;
@@ -16980,13 +17285,13 @@ async function spawnAnalysis2(adapter, model2, prompt, timeoutMs = ANALYSIS_TIME
16980
17285
  let resultText = "";
16981
17286
  let accumulatedText = "";
16982
17287
  await new Promise((resolve) => {
16983
- const proc = spawn6(config2.executable, config2.args, {
17288
+ const proc = spawn7(config2.executable, config2.args, {
16984
17289
  env,
16985
17290
  stdio: ["ignore", "pipe", "pipe"],
16986
17291
  ...config2.cwd ? { cwd: config2.cwd } : {}
16987
17292
  });
16988
17293
  proc.stderr?.resume();
16989
- const rl2 = createInterface5({ input: proc.stdout });
17294
+ const rl2 = createInterface6({ input: proc.stdout });
16990
17295
  const timeout = setTimeout(() => {
16991
17296
  warn(`[optimizer] Analysis timeout (${adapter.id}:${model2})`);
16992
17297
  rl2.close();
@@ -17468,8 +17773,8 @@ var init_ui2 = __esm({
17468
17773
  });
17469
17774
 
17470
17775
  // src/router/optimize.ts
17471
- import { readFileSync as readFileSync13, writeFileSync as writeFileSync7, existsSync as existsSync22 } from "fs";
17472
- import { join as join23 } from "path";
17776
+ import { readFileSync as readFileSync13, writeFileSync as writeFileSync7, existsSync as existsSync22, readdirSync as readdirSync12, unlinkSync as unlinkSync6 } from "fs";
17777
+ import { join as join23, dirname as dirname4 } from "path";
17473
17778
  import { homedir as homedir8 } from "os";
17474
17779
  async function handleOptimizeCommand(chatId, channel, _args) {
17475
17780
  const { getModelDisplayInfo: getModelDisplayInfo2 } = await Promise.resolve().then(() => (init_analyze2(), analyze_exports2));
@@ -17574,13 +17879,38 @@ async function runIdentityAuditFlow(chatId, channel) {
17574
17879
  } = await Promise.resolve().then(() => (init_ui2(), ui_exports));
17575
17880
  const modelInfo = getModelDisplayInfo2(chatId);
17576
17881
  if (!modelInfo) return;
17577
- await channel.sendText(
17882
+ const progressMsgId = typeof channel.sendTextReturningId === "function" ? await channel.sendTextReturningId(
17578
17883
  chatId,
17579
17884
  buildProgressMessage2("identity files", modelInfo.backend, modelInfo.model, modelInfo.thinkingLevel),
17580
- { parseMode: "plain" }
17581
- );
17885
+ "plain"
17886
+ ) : void 0;
17887
+ if (!progressMsgId) {
17888
+ await channel.sendText(
17889
+ chatId,
17890
+ buildProgressMessage2("identity files", modelInfo.backend, modelInfo.model, modelInfo.thinkingLevel),
17891
+ { parseMode: "plain" }
17892
+ );
17893
+ }
17894
+ const startTime = Date.now();
17895
+ const progressInterval = setInterval(async () => {
17896
+ const elapsed = Math.round((Date.now() - startTime) / 1e3);
17897
+ try {
17898
+ if (channel.sendTyping) await channel.sendTyping(chatId);
17899
+ if (progressMsgId && channel.editText) {
17900
+ await channel.editText(
17901
+ chatId,
17902
+ progressMsgId,
17903
+ buildProgressMessage2("identity files", modelInfo.backend, modelInfo.model, modelInfo.thinkingLevel) + `
17904
+ \u23F3 Analyzing... (${elapsed}s)`,
17905
+ "plain"
17906
+ );
17907
+ }
17908
+ } catch {
17909
+ }
17910
+ }, 5e3);
17582
17911
  try {
17583
17912
  const result = await runIdentityAudit2(chatId);
17913
+ clearInterval(progressInterval);
17584
17914
  activeSessions.set(chatId, {
17585
17915
  chatId,
17586
17916
  result,
@@ -17595,6 +17925,7 @@ async function runIdentityAuditFlow(chatId, channel) {
17595
17925
  await channel.sendText(chatId, summary, { parseMode: "plain" });
17596
17926
  }
17597
17927
  } catch (e) {
17928
+ clearInterval(progressInterval);
17598
17929
  await channel.sendText(chatId, `Identity audit failed: ${e}`, { parseMode: "plain" });
17599
17930
  }
17600
17931
  }
@@ -17625,13 +17956,38 @@ async function runSkillAuditFlow(chatId, channel, skillName) {
17625
17956
  const modelInfo = getModelDisplayInfo2(chatId);
17626
17957
  if (!modelInfo) return;
17627
17958
  const skillPath = join23(homedir8(), ".cc-claw", "workspace", "skills", skillName, "SKILL.md");
17628
- await channel.sendText(
17959
+ const progressMsgId = typeof channel.sendTextReturningId === "function" ? await channel.sendTextReturningId(
17629
17960
  chatId,
17630
17961
  buildProgressMessage2(`skill: ${skillName}`, modelInfo.backend, modelInfo.model, modelInfo.thinkingLevel),
17631
- { parseMode: "plain" }
17632
- );
17962
+ "plain"
17963
+ ) : void 0;
17964
+ if (!progressMsgId) {
17965
+ await channel.sendText(
17966
+ chatId,
17967
+ buildProgressMessage2(`skill: ${skillName}`, modelInfo.backend, modelInfo.model, modelInfo.thinkingLevel),
17968
+ { parseMode: "plain" }
17969
+ );
17970
+ }
17971
+ const startTime = Date.now();
17972
+ const progressInterval = setInterval(async () => {
17973
+ const elapsed = Math.round((Date.now() - startTime) / 1e3);
17974
+ try {
17975
+ if (channel.sendTyping) await channel.sendTyping(chatId);
17976
+ if (progressMsgId && channel.editText) {
17977
+ await channel.editText(
17978
+ chatId,
17979
+ progressMsgId,
17980
+ buildProgressMessage2(`skill: ${skillName}`, modelInfo.backend, modelInfo.model, modelInfo.thinkingLevel) + `
17981
+ \u23F3 Analyzing... (${elapsed}s)`,
17982
+ "plain"
17983
+ );
17984
+ }
17985
+ } catch {
17986
+ }
17987
+ }, 5e3);
17633
17988
  try {
17634
17989
  const result = await runSkillAudit2(chatId, skillPath);
17990
+ clearInterval(progressInterval);
17635
17991
  activeSessions.set(chatId, {
17636
17992
  chatId,
17637
17993
  result,
@@ -17646,6 +18002,7 @@ async function runSkillAuditFlow(chatId, channel, skillName) {
17646
18002
  await channel.sendText(chatId, summary, { parseMode: "plain" });
17647
18003
  }
17648
18004
  } catch (e) {
18005
+ clearInterval(progressInterval);
17649
18006
  await channel.sendText(chatId, `Skill audit failed: ${e}`, { parseMode: "plain" });
17650
18007
  }
17651
18008
  }
@@ -17697,8 +18054,28 @@ async function applyFinding(chatId, channel, index) {
17697
18054
  const backupPath = targetPath + `.bak.${Date.now()}`;
17698
18055
  writeFileSync7(backupPath, original, "utf-8");
17699
18056
  pruneBackups2(targetPath);
17700
- const { applyDiff: applyDiff2 } = await Promise.resolve().then(() => (init_apply(), apply_exports));
17701
- const newContent = applyDiff2(original, finding.proposedDiff, "replace");
18057
+ let newContent;
18058
+ try {
18059
+ const { applyWithAI: applyWithAI2 } = await Promise.resolve().then(() => (init_ai_apply(), ai_apply_exports));
18060
+ const aiResult = await applyWithAI2(
18061
+ chatId,
18062
+ original,
18063
+ finding.suggestion || finding.title,
18064
+ finding.proposedDiff,
18065
+ finding.location.split(":")[0] || "unknown"
18066
+ );
18067
+ if (aiResult.success) {
18068
+ newContent = aiResult.newContent;
18069
+ } else {
18070
+ warn(`[optimizer] AI apply failed (${aiResult.error}), falling back to static diff`);
18071
+ const { applyDiff: applyDiff2 } = await Promise.resolve().then(() => (init_apply(), apply_exports));
18072
+ newContent = applyDiff2(original, finding.proposedDiff, "replace");
18073
+ }
18074
+ } catch (aiErr) {
18075
+ warn(`[optimizer] AI apply threw (${aiErr}), falling back to static diff`);
18076
+ const { applyDiff: applyDiff2 } = await Promise.resolve().then(() => (init_apply(), apply_exports));
18077
+ newContent = applyDiff2(original, finding.proposedDiff, "replace");
18078
+ }
17702
18079
  if (newContent === original) {
17703
18080
  await channel.sendText(chatId, `\u26A0\uFE0F Diff produced no changes. The content may have already been modified.`, { parseMode: "plain" });
17704
18081
  session2.skipped.push(index);
@@ -17760,16 +18137,14 @@ function resolveTargetFile(location, auditTarget) {
17760
18137
  return null;
17761
18138
  }
17762
18139
  function pruneBackups2(absolutePath) {
17763
- const { readdirSync: readDir, unlinkSync: unlink4 } = __require("fs");
17764
- const { dirname: dirName } = __require("path");
17765
- const dir = dirName(absolutePath);
18140
+ const dir = dirname4(absolutePath);
17766
18141
  const baseName = absolutePath.split("/").pop() ?? "";
17767
18142
  try {
17768
- const backups = readDir(dir).filter((f) => f.startsWith(baseName + ".bak.")).sort().map((f) => join23(dir, f));
18143
+ const backups = readdirSync12(dir).filter((f) => f.startsWith(baseName + ".bak.")).sort().map((f) => join23(dir, f));
17769
18144
  while (backups.length > 3) {
17770
18145
  const oldest = backups.shift();
17771
18146
  try {
17772
- unlink4(oldest);
18147
+ unlinkSync6(oldest);
17773
18148
  } catch {
17774
18149
  }
17775
18150
  }
@@ -17780,6 +18155,7 @@ var activeSessions;
17780
18155
  var init_optimize = __esm({
17781
18156
  "src/router/optimize.ts"() {
17782
18157
  "use strict";
18158
+ init_log();
17783
18159
  activeSessions = /* @__PURE__ */ new Map();
17784
18160
  }
17785
18161
  });
@@ -17921,6 +18297,7 @@ Tap to toggle:`,
17921
18297
  const exchangeCount = getMessagePairCount(chatId);
17922
18298
  const summarized = await summarizeSession(chatId);
17923
18299
  clearSession(chatId);
18300
+ clearChatPaidSlots(chatId);
17924
18301
  setSessionStartedAt(chatId);
17925
18302
  logActivity(getDb(), { chatId, source: "telegram", eventType: "config_changed", summary: "New session started", detail: { field: "session", action: "reset", summarized } });
17926
18303
  if (typeof channel.sendKeyboard === "function" && oldSessionId) {
@@ -18241,6 +18618,7 @@ Add with: <code>cc-claw ${slotBackend} add-key</code>`, { parseMode: "html" });
18241
18618
  break;
18242
18619
  }
18243
18620
  if (typeof channel.sendKeyboard === "function") {
18621
+ const currentMode = getBackendRotationMode(slotBackend);
18244
18622
  const pinnedSlotId = getChatBackendSlotId(chatId, slotBackend);
18245
18623
  const slotButtons = slots.filter((s) => s.enabled).map((s) => {
18246
18624
  const label2 = s.label || `slot-${s.id}`;
@@ -18253,8 +18631,15 @@ Add with: <code>cc-claw ${slotBackend} add-key</code>`, { parseMode: "html" });
18253
18631
  rows.push(slotButtons.slice(i, i + 2));
18254
18632
  }
18255
18633
  rows.push([{ label: "\u{1F504} Auto (rotation)", data: `bslot:${slotBackend}:auto` }]);
18256
- await channel.sendKeyboard(chatId, `${slotDisplayName} Accounts:`, rows);
18634
+ const modeLabels = { off: "Off", all: "All", accounts: "\u{1F468}\u{1F3FD}\u200D\u{1F4BB} Only", keys: "\u{1F511} Only" };
18635
+ const modeButtons = ["off", "all", "accounts", "keys"].map((m) => ({
18636
+ label: `${m === currentMode ? "\u2713 " : ""}${modeLabels[m]}`,
18637
+ data: `brotation:${slotBackend}:${m}`
18638
+ }));
18639
+ rows.push(modeButtons);
18640
+ await channel.sendKeyboard(chatId, `${slotDisplayName} Accounts & Rotation:`, rows);
18257
18641
  } else {
18642
+ const currentMode = getBackendRotationMode(slotBackend);
18258
18643
  const list = slots.filter((s) => s.enabled).map((s) => {
18259
18644
  const icon = s.slotType === "oauth" ? "\u{1F468}\u{1F3FD}\u200D\u{1F4BB}" : "\u{1F511}";
18260
18645
  return `${icon} ${s.label || `slot-${s.id}`} (#${s.id})`;
@@ -18262,6 +18647,7 @@ Add with: <code>cc-claw ${slotBackend} add-key</code>`, { parseMode: "html" });
18262
18647
  await channel.sendText(chatId, `${slotDisplayName} Slots:
18263
18648
  ${list}
18264
18649
 
18650
+ Rotation mode: ${currentMode}
18265
18651
  Use: /${command} <name> to pin`, { parseMode: "plain" });
18266
18652
  }
18267
18653
  break;
@@ -19952,6 +20338,32 @@ Result: ${task.result.slice(0, 500)}` : ""
19952
20338
  const modeLabels = { off: "Off", all: "All", accounts: "\u{1F468}\u{1F3FD}\u200D\u{1F4BB} Accounts only", keys: "\u{1F511} Keys only" };
19953
20339
  await channel.sendText(chatId, `Rotation mode set to <b>${modeLabels[mode]}</b>.`, { parseMode: "html" });
19954
20340
  return;
20341
+ } else if (data.startsWith("brotation:")) {
20342
+ const parts = data.split(":");
20343
+ const backend2 = parts[1];
20344
+ const mode = parts[2];
20345
+ const validModes = ["off", "all", "accounts", "keys"];
20346
+ if (!validModes.includes(mode)) return;
20347
+ const displayName = backend2.charAt(0).toUpperCase() + backend2.slice(1);
20348
+ const slots = getBackendSlots(backend2);
20349
+ if (mode === "accounts") {
20350
+ const oauthSlots = slots.filter((s) => s.enabled && s.slotType === "oauth");
20351
+ if (oauthSlots.length === 0) {
20352
+ await channel.sendText(chatId, `\u26A0\uFE0F No ${displayName} OAuth accounts configured. Add one with <code>cc-claw ${backend2} add-account</code> or choose a different mode.`, { parseMode: "html" });
20353
+ return;
20354
+ }
20355
+ } else if (mode === "keys") {
20356
+ const keySlots = slots.filter((s) => s.enabled && s.slotType === "api_key");
20357
+ if (keySlots.length === 0) {
20358
+ await channel.sendText(chatId, `\u26A0\uFE0F No ${displayName} API keys configured. Add one with <code>cc-claw ${backend2} add-key</code> or choose a different mode.`, { parseMode: "html" });
20359
+ return;
20360
+ }
20361
+ }
20362
+ setBackendRotationMode(backend2, mode);
20363
+ clearSession(chatId);
20364
+ const modeLabelMap = { off: "Off", all: "All", accounts: "\u{1F468}\u{1F3FD}\u200D\u{1F4BB} Accounts only", keys: "\u{1F511} Keys only" };
20365
+ await channel.sendText(chatId, `${displayName} rotation mode set to <b>${modeLabelMap[mode]}</b>.`, { parseMode: "html" });
20366
+ return;
19955
20367
  } else if (data.startsWith("gslot:")) {
19956
20368
  const val = data.split(":")[1];
19957
20369
  if (val === "auto") {
@@ -20026,6 +20438,37 @@ ${rotationNote}`, { parseMode: "html" });
20026
20438
  }
20027
20439
  }
20028
20440
  return;
20441
+ } else if (data.startsWith("paidslot:")) {
20442
+ const parts = data.split(":");
20443
+ const action = parts[1];
20444
+ const backend2 = parts[2];
20445
+ const displayName = backend2.charAt(0).toUpperCase() + backend2.slice(1);
20446
+ if (action === "approve") {
20447
+ setAllowPaidSlots(chatId, backend2);
20448
+ await channel.sendText(chatId, `\u2705 Paid API key usage approved for ${displayName} (this session). Retrying\u2026`, { parseMode: "html" });
20449
+ const { pendingFallbackMessages: pendingFallbackMessages3 } = await Promise.resolve().then(() => (init_response(), response_exports));
20450
+ const pending = pendingFallbackMessages3.get(chatId);
20451
+ if (pending) {
20452
+ pendingFallbackMessages3.delete(chatId);
20453
+ const { handleMessage: handleMessage2 } = await Promise.resolve().then(() => (init_router(), router_exports));
20454
+ await handleMessage2(pending.msg, pending.channel);
20455
+ }
20456
+ } else if (action === "deny") {
20457
+ const { pendingFallbackMessages: pendingFallbackMessages3 } = await Promise.resolve().then(() => (init_response(), response_exports));
20458
+ pendingFallbackMessages3.delete(chatId);
20459
+ const otherBackends = (await Promise.resolve().then(() => (init_backends(), backends_exports))).getAvailableAdapters().filter((a) => a.id !== backend2);
20460
+ if (typeof channel.sendKeyboard === "function" && otherBackends.length > 0) {
20461
+ const buttons = otherBackends.map((a) => ({
20462
+ label: `\u{1F504} ${a.displayName}`,
20463
+ data: `backend:${a.id}`,
20464
+ style: "primary"
20465
+ }));
20466
+ await channel.sendKeyboard(chatId, `Switch to another backend?`, [buttons]);
20467
+ } else {
20468
+ await channel.sendText(chatId, `Paid usage denied. Try again later or switch backend manually.`, { parseMode: "plain" });
20469
+ }
20470
+ }
20471
+ return;
20029
20472
  } else if (data.startsWith("shell:")) {
20030
20473
  const parts = data.split(":");
20031
20474
  const action = parts[1];
@@ -20501,9 +20944,9 @@ __export(session_log_exports2, {
20501
20944
  startSessionLogCleanupTimer: () => startSessionLogCleanupTimer,
20502
20945
  tailSessionLog: () => tailSessionLog
20503
20946
  });
20504
- import { existsSync as existsSync23, mkdirSync as mkdirSync9, appendFileSync, readdirSync as readdirSync12, unlinkSync as unlinkSync6, statSync as statSync7, createReadStream } from "fs";
20947
+ import { existsSync as existsSync23, mkdirSync as mkdirSync9, appendFileSync, readdirSync as readdirSync13, unlinkSync as unlinkSync7, statSync as statSync7, createReadStream } from "fs";
20505
20948
  import { join as join24, basename as basename3 } from "path";
20506
- import { createInterface as createInterface6 } from "readline";
20949
+ import { createInterface as createInterface7 } from "readline";
20507
20950
  function getRetentionDays() {
20508
20951
  const env = process.env.SESSION_LOG_RETENTION_DAYS;
20509
20952
  if (env) {
@@ -20518,13 +20961,13 @@ function cleanupSessionLogs(retentionDays) {
20518
20961
  const cutoff = Date.now() - days * 24 * 60 * 60 * 1e3;
20519
20962
  let cleaned = 0;
20520
20963
  try {
20521
- for (const file of readdirSync12(SESSION_LOGS_PATH)) {
20964
+ for (const file of readdirSync13(SESSION_LOGS_PATH)) {
20522
20965
  if (!file.startsWith("session-") || !file.endsWith(".log")) continue;
20523
20966
  const filePath = join24(SESSION_LOGS_PATH, file);
20524
20967
  try {
20525
20968
  const { mtimeMs } = statSync7(filePath);
20526
20969
  if (mtimeMs < cutoff) {
20527
- unlinkSync6(filePath);
20970
+ unlinkSync7(filePath);
20528
20971
  cleaned++;
20529
20972
  }
20530
20973
  } catch {
@@ -20548,7 +20991,7 @@ function startSessionLogCleanupTimer() {
20548
20991
  function listSessionLogs() {
20549
20992
  if (!existsSync23(SESSION_LOGS_PATH)) return [];
20550
20993
  const logs = [];
20551
- for (const file of readdirSync12(SESSION_LOGS_PATH)) {
20994
+ for (const file of readdirSync13(SESSION_LOGS_PATH)) {
20552
20995
  if (!file.startsWith("session-") || !file.endsWith(".log")) continue;
20553
20996
  const filePath = join24(SESSION_LOGS_PATH, file);
20554
20997
  try {
@@ -20574,7 +21017,7 @@ async function* tailSessionLog(filePath, lines = 50) {
20574
21017
  return;
20575
21018
  }
20576
21019
  const allLines = [];
20577
- const rl2 = createInterface6({
21020
+ const rl2 = createInterface7({
20578
21021
  input: createReadStream(filePath, { encoding: "utf-8" }),
20579
21022
  crlfDelay: Infinity
20580
21023
  });
@@ -21399,6 +21842,33 @@ You're still in discussion mode \u2014 try again or click a button to exit.`, {
21399
21842
  }
21400
21843
  return;
21401
21844
  }
21845
+ if (errMsg.startsWith(FREE_SLOTS_EXHAUSTED)) {
21846
+ const parts = errMsg.split("|");
21847
+ const backend2 = parts[1] ?? "unknown";
21848
+ const paidCount = parts[2] ?? "?";
21849
+ const displayName = backend2.charAt(0).toUpperCase() + backend2.slice(1);
21850
+ const { pendingFallbackMessages: pendingFallbackMessages3 } = await Promise.resolve().then(() => (init_response(), response_exports));
21851
+ pendingFallbackMessages3.set(chatId, { msg, channel, agentMode: effectiveAgentMode });
21852
+ if (typeof channel.sendKeyboard === "function") {
21853
+ await channel.sendKeyboard(
21854
+ chatId,
21855
+ `\u26A0\uFE0F All free ${displayName} accounts are exhausted.
21856
+
21857
+ ${paidCount} paid API key slot(s) available. Using them will incur API costs.
21858
+
21859
+ Approve paid usage for this session?`,
21860
+ [
21861
+ [
21862
+ { label: "\u2705 Approve (this session)", data: `paidslot:approve:${backend2}`, style: "success" },
21863
+ { label: "\u274C No, switch backend", data: `paidslot:deny:${backend2}`, style: "danger" }
21864
+ ]
21865
+ ]
21866
+ );
21867
+ } else {
21868
+ await channel.sendText(chatId, `\u26A0\uFE0F All free ${displayName} accounts exhausted. ${paidCount} paid API key slot(s) available but not approved. Switch backend or approve paid usage.`, { parseMode: "plain" });
21869
+ }
21870
+ return;
21871
+ }
21402
21872
  const errorClass = classifyError(err);
21403
21873
  if (errorClass === "exhausted") {
21404
21874
  if (await handleResponseExhaustion(errMsg, chatId, msg, channel)) return;
@@ -21742,6 +22212,10 @@ async function runWithRetry(job, model2, runId, t0) {
21742
22212
  onToolAction = makeToolActionCallback2(chatId, channel, vLevel);
21743
22213
  }
21744
22214
  }
22215
+ if (job.allowPaidSlots) {
22216
+ const cronBackend = currentBackend;
22217
+ setAllowPaidSlots(chatId, cronBackend);
22218
+ }
21745
22219
  const response = await askAgent(chatId, job.description, {
21746
22220
  model: currentModel,
21747
22221
  backend: currentBackend,
@@ -21750,6 +22224,9 @@ async function runWithRetry(job, model2, runId, t0) {
21750
22224
  permMode: job.sessionType === "main" ? getMode(job.chatId) : "yolo",
21751
22225
  onToolAction
21752
22226
  });
22227
+ if (job.allowPaidSlots) {
22228
+ clearChatPaidSlots(chatId);
22229
+ }
21753
22230
  if (isFallback) {
21754
22231
  response.text = `[Fallback: ran on ${currentBackend}:${currentModel}]
21755
22232
 
@@ -21759,8 +22236,13 @@ ${response.text}`;
21759
22236
  } catch (err) {
21760
22237
  lastError = err;
21761
22238
  const errorClass = classifyError(err);
22239
+ const errMsg = errorMessage(err);
22240
+ if (typeof errMsg === "string" && errMsg.startsWith(FREE_SLOTS_EXHAUSTED)) {
22241
+ log(`[scheduler] Job #${job.id} backend ${currentBackend} free slots exhausted (paid not approved): ${errMsg}`);
22242
+ break;
22243
+ }
21762
22244
  if (errorClass === "exhausted") {
21763
- log(`[scheduler] Job #${job.id} backend ${currentBackend} exhausted: ${errorMessage(err)}`);
22245
+ log(`[scheduler] Job #${job.id} backend ${currentBackend} exhausted: ${errMsg}`);
21764
22246
  break;
21765
22247
  }
21766
22248
  if (errorClass === "permanent" || attempt >= MAX_RETRIES) {
@@ -21962,7 +22444,7 @@ var init_wrap_backend = __esm({
21962
22444
  });
21963
22445
 
21964
22446
  // src/agents/runners/config-loader.ts
21965
- import { readFileSync as readFileSync14, readdirSync as readdirSync13, existsSync as existsSync24, mkdirSync as mkdirSync10, watchFile, unwatchFile } from "fs";
22447
+ import { readFileSync as readFileSync14, readdirSync as readdirSync14, existsSync as existsSync24, mkdirSync as mkdirSync10, watchFile, unwatchFile } from "fs";
21966
22448
  import { join as join26 } from "path";
21967
22449
  import { execFileSync as execFileSync2 } from "child_process";
21968
22450
  function resolveExecutable(config2) {
@@ -22116,7 +22598,7 @@ function loadAllRunnerConfigs() {
22116
22598
  mkdirSync10(RUNNERS_PATH, { recursive: true });
22117
22599
  return [];
22118
22600
  }
22119
- const files = readdirSync13(RUNNERS_PATH).filter((f) => f.endsWith(".json"));
22601
+ const files = readdirSync14(RUNNERS_PATH).filter((f) => f.endsWith(".json"));
22120
22602
  const configs = [];
22121
22603
  for (const file of files) {
22122
22604
  const config2 = loadRunnerConfig(join26(RUNNERS_PATH, file));
@@ -22147,7 +22629,7 @@ function watchRunnerConfigs(onChange) {
22147
22629
  watchedFiles.delete(prev);
22148
22630
  }
22149
22631
  }
22150
- const files = readdirSync13(RUNNERS_PATH).filter((f) => f.endsWith(".json"));
22632
+ const files = readdirSync14(RUNNERS_PATH).filter((f) => f.endsWith(".json"));
22151
22633
  for (const file of files) {
22152
22634
  const fullPath = join26(RUNNERS_PATH, file);
22153
22635
  if (watchedFiles.has(fullPath)) continue;
@@ -23814,6 +24296,7 @@ async function main() {
23814
24296
  }
23815
24297
  log(`[cc-claw] Starting v${version}`);
23816
24298
  initDatabase();
24299
+ clearAllPaidSlots();
23817
24300
  pruneMessageLog(30, 2e3);
23818
24301
  bootstrapBuiltinMcps(getDb());
23819
24302
  try {
@@ -24136,7 +24619,7 @@ __export(service_exports, {
24136
24619
  serviceStatus: () => serviceStatus,
24137
24620
  uninstallService: () => uninstallService
24138
24621
  });
24139
- import { existsSync as existsSync29, mkdirSync as mkdirSync13, writeFileSync as writeFileSync9, unlinkSync as unlinkSync7 } from "fs";
24622
+ import { existsSync as existsSync29, mkdirSync as mkdirSync13, writeFileSync as writeFileSync9, unlinkSync as unlinkSync8 } from "fs";
24140
24623
  import { execFileSync as execFileSync3, execSync as execSync6 } from "child_process";
24141
24624
  import { homedir as homedir10, platform } from "os";
24142
24625
  import { join as join30, dirname as dirname6 } from "path";
@@ -24245,7 +24728,7 @@ function uninstallMacOS() {
24245
24728
  execFileSync3("launchctl", ["unload", PLIST_PATH]);
24246
24729
  } catch {
24247
24730
  }
24248
- unlinkSync7(PLIST_PATH);
24731
+ unlinkSync8(PLIST_PATH);
24249
24732
  console.log(" Service uninstalled.");
24250
24733
  }
24251
24734
  function formatUptime(seconds) {
@@ -24334,7 +24817,7 @@ function uninstallLinux() {
24334
24817
  execFileSync3("systemctl", ["--user", "disable", "cc-claw"]);
24335
24818
  } catch {
24336
24819
  }
24337
- unlinkSync7(UNIT_PATH);
24820
+ unlinkSync8(UNIT_PATH);
24338
24821
  execFileSync3("systemctl", ["--user", "daemon-reload"]);
24339
24822
  console.log(" Service uninstalled.");
24340
24823
  }
@@ -25100,7 +25583,7 @@ __export(gemini_exports, {
25100
25583
  });
25101
25584
  import { existsSync as existsSync34, mkdirSync as mkdirSync14, writeFileSync as writeFileSync10, readFileSync as readFileSync22, chmodSync } from "fs";
25102
25585
  import { join as join31 } from "path";
25103
- import { createInterface as createInterface7 } from "readline";
25586
+ import { createInterface as createInterface8 } from "readline";
25104
25587
  function requireDb() {
25105
25588
  if (!existsSync34(DB_PATH)) {
25106
25589
  outputError("DB_NOT_FOUND", "Database not found. Run cc-claw setup first.");
@@ -25173,7 +25656,7 @@ async function geminiList(globalOpts) {
25173
25656
  }
25174
25657
  async function geminiAddKey(globalOpts, opts) {
25175
25658
  await requireWriteDb();
25176
- const rl2 = createInterface7({ input: process.stdin, output: process.stdout });
25659
+ const rl2 = createInterface8({ input: process.stdin, output: process.stdout });
25177
25660
  const ask2 = (q) => new Promise((r) => rl2.question(q, r));
25178
25661
  const key = await ask2("Paste your Gemini API key: ");
25179
25662
  rl2.close();
@@ -25365,7 +25848,7 @@ __export(backend_cmd_factory_exports, {
25365
25848
  });
25366
25849
  import { existsSync as existsSync35, mkdirSync as mkdirSync15, readFileSync as readFileSync23 } from "fs";
25367
25850
  import { join as join32 } from "path";
25368
- import { createInterface as createInterface8 } from "readline";
25851
+ import { createInterface as createInterface9 } from "readline";
25369
25852
  function requireDb2() {
25370
25853
  if (!existsSync35(DB_PATH)) {
25371
25854
  outputError("DB_NOT_FOUND", "Database not found. Run cc-claw setup first.");
@@ -25426,7 +25909,7 @@ Add one with: cc-claw ${backend2} add-account or cc-claw ${backend2} add-key`)
25426
25909
  function makeAddKey(backend2, displayName) {
25427
25910
  return async function addKey(_globalOpts, opts) {
25428
25911
  await requireWriteDb2();
25429
- const rl2 = createInterface8({ input: process.stdin, output: process.stdout });
25912
+ const rl2 = createInterface9({ input: process.stdin, output: process.stdout });
25430
25913
  const ask2 = (q) => new Promise((r) => rl2.question(q, r));
25431
25914
  const key = await ask2(`Paste your ${displayName} API key: `);
25432
25915
  rl2.close();
@@ -27641,7 +28124,7 @@ var tui_exports = {};
27641
28124
  __export(tui_exports, {
27642
28125
  tuiCommand: () => tuiCommand
27643
28126
  });
27644
- import { createInterface as createInterface9 } from "readline";
28127
+ import { createInterface as createInterface10 } from "readline";
27645
28128
  import pc2 from "picocolors";
27646
28129
  async function tuiCommand(globalOpts, cmdOpts) {
27647
28130
  const { isDaemonRunning: isDaemonRunning2, apiPost: apiPost2 } = await Promise.resolve().then(() => (init_api_client(), api_client_exports));
@@ -27651,7 +28134,7 @@ async function tuiCommand(globalOpts, cmdOpts) {
27651
28134
  }
27652
28135
  const chatId = resolveChatId(globalOpts);
27653
28136
  const { chatSend: chatSend2 } = await Promise.resolve().then(() => (init_chat2(), chat_exports2));
27654
- const rl2 = createInterface9({
28137
+ const rl2 = createInterface10({
27655
28138
  input: process.stdin,
27656
28139
  output: process.stdout,
27657
28140
  prompt: pc2.cyan("you > "),
@@ -28452,7 +28935,7 @@ var init_optimize2 = __esm({
28452
28935
  var setup_exports = {};
28453
28936
  import { existsSync as existsSync55, writeFileSync as writeFileSync12, readFileSync as readFileSync26, copyFileSync as copyFileSync4, mkdirSync as mkdirSync18, statSync as statSync12 } from "fs";
28454
28937
  import { execFileSync as execFileSync5 } from "child_process";
28455
- import { createInterface as createInterface10 } from "readline";
28938
+ import { createInterface as createInterface11 } from "readline";
28456
28939
  import { join as join34 } from "path";
28457
28940
  function divider2() {
28458
28941
  console.log(dim("\u2500".repeat(55)));
@@ -28815,7 +29298,7 @@ var init_setup = __esm({
28815
29298
  "src/setup.ts"() {
28816
29299
  "use strict";
28817
29300
  init_paths();
28818
- rl = createInterface10({ input: process.stdin, output: process.stdout });
29301
+ rl = createInterface11({ input: process.stdin, output: process.stdout });
28819
29302
  ask = (q) => new Promise((resolve) => rl.question(q, resolve));
28820
29303
  bold = (s) => `\x1B[1m${s}\x1B[0m`;
28821
29304
  green = (s) => `\x1B[32m${s}\x1B[0m`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cc-claw",
3
- "version": "0.18.1",
3
+ "version": "0.18.3",
4
4
  "description": "CC-Claw: Personal AI assistant on Telegram — multi-backend (Claude, Gemini, Codex, Cursor), sub-agent orchestration, MCP management",
5
5
  "type": "module",
6
6
  "main": "dist/cli.js",