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.
- package/dist/cli.js +558 -75
- 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.
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
10897
|
-
import { createInterface as
|
|
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 =
|
|
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 =
|
|
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
|
|
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
|
|
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
|
|
11417
|
-
const
|
|
11418
|
-
const
|
|
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,
|
|
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,
|
|
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,
|
|
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
|
|
16938
|
-
import { createInterface as
|
|
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
|
|
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 =
|
|
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 =
|
|
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.
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
17701
|
-
|
|
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
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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 =
|
|
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: ${
|
|
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
|
|
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 =
|
|
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 =
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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 =
|
|
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
|
|
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 =
|
|
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
|
|
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 =
|
|
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
|
|
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 =
|
|
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